Version 2.14.0-243.0.dev
Merge commit '10354d0aefdd6ec6f5a63e1fa4b4af6e313e53f8' into 'dev'
diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json
index efcb595..fe124c6 100644
--- a/.dart_tool/package_config.json
+++ b/.dart_tool/package_config.json
@@ -470,7 +470,7 @@
"name": "nnbd_migration",
"rootUri": "../pkg/nnbd_migration",
"packageUri": "lib/",
- "languageVersion": "2.6"
+ "languageVersion": "2.12"
},
{
"name": "oauth2",
diff --git a/pkg/analysis_server/test/abstract_context.dart b/pkg/analysis_server/test/abstract_context.dart
index b2db401..f628981 100644
--- a/pkg/analysis_server/test/abstract_context.dart
+++ b/pkg/analysis_server/test/abstract_context.dart
@@ -71,7 +71,7 @@
AnalysisSession get session => contextFor('/home/test').currentSession;
- String? get testPackageLanguageVersion => '2.9';
+ String? get testPackageLanguageVersion => latestLanguageVersion;
String get testPackageLibPath => '$testPackageRootPath/lib';
diff --git a/pkg/analysis_server/test/plugin/protocol_dart_test.dart b/pkg/analysis_server/test/plugin/protocol_dart_test.dart
index bfaddb0..2f7ae488 100644
--- a/pkg/analysis_server/test/plugin/protocol_dart_test.dart
+++ b/pkg/analysis_server/test/plugin/protocol_dart_test.dart
@@ -22,6 +22,9 @@
@reflectiveTest
class ConvertElementNullableTest extends AbstractSingleUnitTest {
+ @override
+ String? get testPackageLanguageVersion => '2.9';
+
Future<void> test_CONSTRUCTOR_required_parameters_1() async {
writeTestPackageConfig(meta: true);
await resolveTestCode('''
diff --git a/pkg/analysis_server/test/services/completion/postfix/postfix_completion_test.dart b/pkg/analysis_server/test/services/completion/postfix/postfix_completion_test.dart
index e4912a8..f09ca3f 100644
--- a/pkg/analysis_server/test/services/completion/postfix/postfix_completion_test.dart
+++ b/pkg/analysis_server/test/services/completion/postfix/postfix_completion_test.dart
@@ -469,6 +469,9 @@
@reflectiveTest
class _NotNullTest extends PostfixCompletionTest {
+ @override
+ String? get testPackageLanguageVersion => '2.9';
+
Future<void> test_nn() async {
await _prepareCompletion('.nn', '''
f() {
@@ -574,6 +577,9 @@
@reflectiveTest
class _ParenTest extends PostfixCompletionTest {
+ @override
+ String? get testPackageLanguageVersion => '2.9';
+
Future<void> test_paren() async {
await _prepareCompletion('.par', '''
f(expr) {
@@ -590,6 +596,9 @@
@reflectiveTest
class _ReturnTest extends PostfixCompletionTest {
+ @override
+ String? get testPackageLanguageVersion => '2.9';
+
Future<void> test_return() async {
await _prepareCompletion('.return', '''
f(expr) {
@@ -606,6 +615,9 @@
@reflectiveTest
class _SwitchTest extends PostfixCompletionTest {
+ @override
+ String? get testPackageLanguageVersion => '2.9';
+
Future<void> test_return() async {
await _prepareCompletion('.switch', '''
f(expr) {
diff --git a/pkg/analysis_server/test/services/correction/name_suggestion_test.dart b/pkg/analysis_server/test/services/correction/name_suggestion_test.dart
index 243fd9b..d304e5f 100644
--- a/pkg/analysis_server/test/services/correction/name_suggestion_test.dart
+++ b/pkg/analysis_server/test/services/correction/name_suggestion_test.dart
@@ -33,7 +33,7 @@
await resolveTestCode('''
class TreeNode {}
main() {
- TreeNode node = null;
+ TreeNode? node = null;
}
''');
var excluded = <String>{};
@@ -103,20 +103,20 @@
await resolveTestCode('''
class A {
void build() {
- List l = new List();
+ Map l = Map();
}
}
''');
var excluded = <String>{};
- var expr = findNode.instanceCreation('new List');
+ var expr = findNode.instanceCreation('Map(');
expect(
getVariableNameSuggestionsForExpression(null, expr, excluded,
isMethod: false),
- unorderedEquals(['list']));
+ unorderedEquals(['map']));
expect(
getVariableNameSuggestionsForExpression(null, expr, excluded,
isMethod: true),
- unorderedEquals(['buildList']));
+ unorderedEquals(['buildMap']));
}
Future<void> test_forExpression_indexExpression_endsWithE() async {
diff --git a/pkg/analysis_server/test/services/correction/util_test.dart b/pkg/analysis_server/test/services/correction/util_test.dart
index fcbee98..d6dc428 100644
--- a/pkg/analysis_server/test/services/correction/util_test.dart
+++ b/pkg/analysis_server/test/services/correction/util_test.dart
@@ -23,8 +23,9 @@
Future<void> assert_invertCondition(String expr, String expected) async {
await resolveTestCode('''
main() {
- int v1, v2, v3, v4, v5;
- bool b1, b2, b3, b4, b5;
+ int? v1, v2, v3, v4, v5;
+ bool b1 = true, b2 = true, b3 = true;
+ bool? b4, b5;
if ($expr) {
0;
} else {
@@ -281,8 +282,8 @@
}
Future<void> test_invertCondition_binary_compare_boolean() async {
- await assert_invertCondition('b1 == null', 'b1 != null');
- await assert_invertCondition('b1 != null', 'b1 == null');
+ await assert_invertCondition('b4 == null', 'b4 != null');
+ await assert_invertCondition('b4 != null', 'b4 == null');
}
Future<void> test_invertCondition_binary_logical() async {
diff --git a/pkg/analysis_server/test/services/search/element_visitors_test.dart b/pkg/analysis_server/test/services/search/element_visitors_test.dart
index 0de43dd0..372dac6 100644
--- a/pkg/analysis_server/test/services/search/element_visitors_test.dart
+++ b/pkg/analysis_server/test/services/search/element_visitors_test.dart
@@ -51,12 +51,12 @@
Future<void> test_topLevelVariable() async {
await resolveTestCode(r'''
-int aaa, bbb;
-int ccc;
+int? aaa, bbb;
+int? ccc;
''');
- _assertElement(4, ElementKind.TOP_LEVEL_VARIABLE, 'aaa');
- _assertElement(9, ElementKind.TOP_LEVEL_VARIABLE, 'bbb');
- _assertElement(18, ElementKind.TOP_LEVEL_VARIABLE, 'ccc');
+ _assertElement(5, ElementKind.TOP_LEVEL_VARIABLE, 'aaa');
+ _assertElement(10, ElementKind.TOP_LEVEL_VARIABLE, 'bbb');
+ _assertElement(20, ElementKind.TOP_LEVEL_VARIABLE, 'ccc');
}
void _assertElement(int nameOffset, ElementKind kind, String name) {
diff --git a/pkg/analysis_server/test/services/search/hierarchy_test.dart b/pkg/analysis_server/test/services/search/hierarchy_test.dart
index 0280926..4af0fe9 100644
--- a/pkg/analysis_server/test/services/search/hierarchy_test.dart
+++ b/pkg/analysis_server/test/services/search/hierarchy_test.dart
@@ -77,7 +77,7 @@
Future<void> test_getHierarchyMembers_fields() async {
await _indexTestUnit('''
class A {
- int foo;
+ int? foo;
}
class B extends A {
get foo => null;
@@ -86,7 +86,7 @@
set foo(x) {}
}
class D {
- int foo;
+ int? foo;
}
''');
var classA = findElement.class_('A');
@@ -115,7 +115,7 @@
Future<void> test_getHierarchyMembers_fields_static() async {
await _indexTestUnit('''
class A {
- static int foo;
+ static int? foo;
}
class B extends A {
static get foo => null;
diff --git a/pkg/analysis_server/test/src/services/correction/assist/add_diagnostic_property_reference_test.dart b/pkg/analysis_server/test/src/services/correction/assist/add_diagnostic_property_reference_test.dart
index f9b684c..40837a6 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/add_diagnostic_property_reference_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/add_diagnostic_property_reference_test.dart
@@ -34,7 +34,7 @@
import 'package:flutter/widgets.dart';
class W extends Widget {
- bool /*caret*/property;
+ bool /*caret*/property = true;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
@@ -46,7 +46,7 @@
import 'package:flutter/widgets.dart';
class W extends Widget {
- bool property;
+ bool property = true;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
@@ -69,7 +69,7 @@
Future<void> test_notAvailable_outsideDiagnosticable() async {
await resolveTestCode('''
class C {
- String get f/*caret*/ => null;
+ String get f/*caret*/ => '';
}
''');
await assertNoAssist();
diff --git a/pkg/analysis_server/test/src/services/correction/assist/add_not_null_assert_test.dart b/pkg/analysis_server/test/src/services/correction/assist/add_not_null_assert_test.dart
index d100c48..bc269f0 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/add_not_null_assert_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/add_not_null_assert_test.dart
@@ -41,7 +41,7 @@
Future<void> test_function_withAssert() async {
await resolveTestCode('''
-foo(int x) {
+foo(int? x) {
assert(x != null);
}
''');
@@ -50,7 +50,7 @@
Future<void> test_function_withAssert2() async {
await resolveTestCode('''
-foo(int x) {
+foo(int? x) {
print('foo');
assert(x != null);
}
diff --git a/pkg/analysis_server/test/src/services/correction/assist/convert_into_expression_body_test.dart b/pkg/analysis_server/test/src/services/correction/assist/convert_into_expression_body_test.dart
index 739509b..fc5175e 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/convert_into_expression_body_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/convert_into_expression_body_test.dart
@@ -112,7 +112,7 @@
Future<void> test_constructor_generative() async {
await resolveTestCode('''
class A {
- int x;
+ int x = 0;
A() {
x = 3;
diff --git a/pkg/analysis_server/test/src/services/correction/assist/convert_into_final_field_test.dart b/pkg/analysis_server/test/src/services/correction/assist/convert_into_final_field_test.dart
index 345eda1..c80b319 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/convert_into_final_field_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/convert_into_final_field_test.dart
@@ -57,7 +57,7 @@
void set foo(_) {}
}
class B extends A {
- int get foo => null;
+ int get foo => 3;
}
''');
await assertHasAssistAt('get foo', '''
@@ -65,7 +65,7 @@
void set foo(_) {}
}
class B extends A {
- final int foo;
+ final int foo = 3;
}
''');
}
@@ -73,7 +73,7 @@
Future<void> test_hasSetter_inThisClass() async {
await resolveTestCode('''
class A {
- int get foo => null;
+ int get foo => 0;
void set foo(_) {}
}
''');
@@ -143,12 +143,12 @@
Future<void> test_null() async {
await resolveTestCode('''
class A {
- int get foo => null;
+ int? get foo => null;
}
''');
await assertHasAssistAt('get foo', '''
class A {
- final int foo;
+ final int? foo;
}
''');
}
@@ -169,12 +169,12 @@
Future<void> test_onReturnType_parameterized() async {
await resolveTestCode('''
class A {
- List<int> get foo => null;
+ List<int>? get foo => null;
}
''');
- await assertHasAssistAt('nt> get', '''
+ await assertHasAssistAt('nt>? get', '''
class A {
- final List<int> foo;
+ final List<int>? foo;
}
''');
}
diff --git a/pkg/analysis_server/test/src/services/correction/assist/convert_into_is_not_empty_test.dart b/pkg/analysis_server/test/src/services/correction/assist/convert_into_is_not_empty_test.dart
index ce777b0..e8650da 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/convert_into_is_not_empty_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/convert_into_is_not_empty_test.dart
@@ -22,7 +22,7 @@
Future<void> test_noBang() async {
verifyNoTestUnitErrors = false;
await resolveTestCode('''
-main(String str) {
+void f(String str) {
~str.isEmpty;
}
''');
@@ -34,7 +34,7 @@
class A {
bool get isEmpty => false;
}
-main(A a) {
+void f(A a) {
!a.isEmpty;
}
''');
@@ -43,7 +43,7 @@
Future<void> test_notInPrefixExpression() async {
await resolveTestCode('''
-main(String str) {
+void f(String str) {
str.isEmpty;
}
''');
@@ -52,7 +52,7 @@
Future<void> test_notIsEmpty() async {
await resolveTestCode('''
-main(int p) {
+void f(int p) {
!p.isEven;
}
''');
@@ -61,12 +61,12 @@
Future<void> test_on_isEmpty() async {
await resolveTestCode('''
-main(String str) {
+void f(String str) {
!str.isEmpty;
}
''');
await assertHasAssistAt('isEmpty', '''
-main(String str) {
+void f(String str) {
str.isNotEmpty;
}
''');
@@ -74,12 +74,12 @@
Future<void> test_on_str() async {
await resolveTestCode('''
-main(String str) {
+void f(String str) {
!str.isEmpty;
}
''');
await assertHasAssistAt('str.', '''
-main(String str) {
+void f(String str) {
str.isNotEmpty;
}
''');
@@ -87,12 +87,12 @@
Future<void> test_propertyAccess() async {
await resolveTestCode('''
-main(String str) {
+void f(String str) {
!'text'.isEmpty;
}
''');
await assertHasAssistAt('isEmpty', '''
-main(String str) {
+void f(String str) {
'text'.isNotEmpty;
}
''');
diff --git a/pkg/analysis_server/test/src/services/correction/assist/convert_to_list_literal_test.dart b/pkg/analysis_server/test/src/services/correction/assist/convert_to_list_literal_test.dart
index 880a6f1..875741d 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/convert_to_list_literal_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/convert_to_list_literal_test.dart
@@ -19,6 +19,11 @@
@override
AssistKind get kind => DartAssistKind.CONVERT_TO_LIST_LITERAL;
+ @override
+ // This assist doesn't apply in null safety because the default List
+ // constructor is removed.
+ String? get testPackageLanguageVersion => '2.9';
+
Future<void> test_default_declaredType() async {
await resolveTestCode('''
List l = Li/*caret*/st();
diff --git a/pkg/analysis_server/test/src/services/correction/assist/convert_to_null_aware_test.dart b/pkg/analysis_server/test/src/services/correction/assist/convert_to_null_aware_test.dart
index 7d6b659..b8164ad 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/convert_to_null_aware_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/convert_to_null_aware_test.dart
@@ -25,9 +25,9 @@
abstract class A {
int m();
}
-int f(A a1, A a2) => a1 == null ? null : a2.m();
+int? f(A? a1, A a2) => a1 == null ? null : a2.m();
''');
- await assertNoAssistAt('?');
+ await assertNoAssistAt('? n');
}
Future<void> test_equal_notComparedToNull() async {
@@ -43,9 +43,9 @@
Future<void> test_equal_notIdentifier() async {
await resolveTestCode('''
abstract class A {
- int m();
+ int? m();
}
-int f(A a) => a.m() == null ? 0 : a.m();
+int? f(A a) => a.m() == null ? 0 : a.m();
''');
await assertNoAssistAt('?');
}
@@ -53,10 +53,9 @@
Future<void> test_equal_notInvocation() async {
await resolveTestCode('''
abstract class A {
- int m();
int operator +(A a);
}
-int f(A a1) => a1 == null ? null : a1 + a1;
+int? f(A? a1) => a1 == null ? null : a1 + a1;
''');
await assertNoAssistAt('?');
}
@@ -66,19 +65,20 @@
abstract class A {
int m();
}
-int f(A a1, A a2) => a1 == null ? a2.m() : a1.m();
+int f(A? a1, A a2) => a1 == null ? a2.m() : a1.m();
''');
await assertNoAssistAt('?');
}
Future<void> test_equal_notPeriod() async {
+ verifyNoTestUnitErrors = false;
await resolveTestCode('''
abstract class A {
int m();
}
-int f(A a1) => a1 == null ? null : a1?.m();
+int? f(A? a1) => a1 == null ? null : a1?.m();
''');
- await assertNoAssistAt('? ');
+ await assertNoAssistAt('? n');
}
Future<void> test_equal_nullOnLeft() async {
@@ -86,13 +86,13 @@
abstract class A {
int m();
}
-int f(A a) => null == a ? null : a.m();
+int? f(A? a) => null == a ? null : a.m();
''');
- await assertHasAssistAt('?', '''
+ await assertHasAssistAt('? n', '''
abstract class A {
int m();
}
-int f(A a) => a?.m();
+int? f(A? a) => a?.m();
''');
}
@@ -103,7 +103,7 @@
abstract class A {
int m();
}
-int f(A a) => null == a ? null : a.m();
+int? f(A? a) => null == a ? null : a.m();
''');
await assertNoAssist();
}
@@ -113,28 +113,28 @@
abstract class A {
int m();
}
-int f(A a) => a == null ? null : a.m();
+int? f(A? a) => a == null ? null : a.m();
''');
- await assertHasAssistAt('?', '''
+ await assertHasAssistAt('? n', '''
abstract class A {
int m();
}
-int f(A a) => a?.m();
+int? f(A? a) => a?.m();
''');
}
Future<void> test_equal_prefixedIdentifier() async {
await resolveTestCode('''
class A {
- int p;
+ int p = 0;
}
-int f(A a) => null == a ? null : a.p;
+int? f(A? a) => null == a ? null : a.p;
''');
- await assertHasAssistAt('?', '''
+ await assertHasAssistAt('? n', '''
class A {
- int p;
+ int p = 0;
}
-int f(A a) => a?.p;
+int? f(A? a) => a?.p;
''');
}
@@ -155,7 +155,7 @@
abstract class A {
int m();
}
-int f(A a1, A a2) => a1 != null ? a1.m() : a2.m();
+int f(A? a1, A a2) => a1 != null ? a1.m() : a2.m();
''');
await assertNoAssistAt('?');
}
@@ -165,13 +165,13 @@
abstract class A {
int m();
}
-int f(A a) => null != a ? a.m() : null;
+int? f(A? a) => null != a ? a.m() : null;
''');
- await assertHasAssistAt('?', '''
+ await assertHasAssistAt('? a.', '''
abstract class A {
int m();
}
-int f(A a) => a?.m();
+int? f(A? a) => a?.m();
''');
}
@@ -180,13 +180,13 @@
abstract class A {
int m();
}
-int f(A a) => a != null ? a.m() : null;
+int? f(A? a) => a != null ? a.m() : null;
''');
- await assertHasAssistAt('?', '''
+ await assertHasAssistAt('? a.', '''
abstract class A {
int m();
}
-int f(A a) => a?.m();
+int? f(A? a) => a?.m();
''');
}
}
diff --git a/pkg/analysis_server/test/src/services/correction/assist/convert_to_spread_test.dart b/pkg/analysis_server/test/src/services/correction/assist/convert_to_spread_test.dart
index dc43575..861a422 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/convert_to_spread_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/convert_to_spread_test.dart
@@ -22,12 +22,12 @@
Future<void> test_addAll_condition_const() async {
await resolveTestCode('''
-bool condition;
+bool condition = false;
var things;
var l = ['a']..add/*caret*/All(condition ? things : const []);
''');
await assertHasAssist('''
-bool condition;
+bool condition = false;
var things;
var l = ['a', if (condition) ...things];
''');
@@ -35,12 +35,12 @@
Future<void> test_addAll_condition_nonConst() async {
await resolveTestCode('''
-bool condition;
+bool condition = false;
var things;
var l = ['a']..add/*caret*/All(condition ? things : []);
''');
await assertHasAssist('''
-bool condition;
+bool condition = false;
var things;
var l = ['a', if (condition) ...things];
''');
diff --git a/pkg/analysis_server/test/src/services/correction/assist/encapsulate_field_test.dart b/pkg/analysis_server/test/src/services/correction/assist/encapsulate_field_test.dart
index 5817ae2..fc3f755 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/encapsulate_field_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/encapsulate_field_test.dart
@@ -24,7 +24,7 @@
class A {
int _test = 42;
}
-main(A a) {
+void f(A a) {
print(a._test);
}
''');
@@ -36,14 +36,14 @@
class A {
/// AAA
/// BBB
- int test;
+ int test = 0;
}
''');
- await assertHasAssistAt('test;', '''
+ await assertHasAssistAt('test', '''
class A {
/// AAA
/// BBB
- int _test;
+ int _test = 0;
/// AAA
/// BBB
@@ -83,7 +83,7 @@
int test = 42;
A(this.test);
}
-main(A a) {
+void f(A a) {
print(a.test);
}
''');
@@ -98,7 +98,7 @@
}
A(this._test);
}
-main(A a) {
+void f(A a) {
print(a.test);
}
''');
@@ -126,13 +126,13 @@
Future<void> test_multipleFields() async {
await resolveTestCode('''
class A {
- int aaa, bbb, ccc;
+ int aaa = 0, bbb = 0, ccc = 0;
}
-main(A a) {
+void f(A a) {
print(a.bbb);
}
''');
- await assertNoAssistAt('bbb, ');
+ await assertNoAssistAt('bbb ');
}
Future<void> test_notOnName() async {
@@ -149,7 +149,7 @@
class A {
var test = 42;
}
-main(A a) {
+void f(A a) {
print(a.test);
}
''');
@@ -163,7 +163,7 @@
_test = test;
}
}
-main(A a) {
+void f(A a) {
print(a.test);
}
''');
diff --git a/pkg/analysis_server/test/src/services/correction/assist/exchange_operands_test.dart b/pkg/analysis_server/test/src/services/correction/assist/exchange_operands_test.dart
index c5921f3..a3075a8 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/exchange_operands_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/exchange_operands_test.dart
@@ -26,12 +26,12 @@
var initialOperator = initialOperators[i];
var resultOperator = resultOperators[i];
await resolveTestCode('''
-bool main(int a, int b) {
+bool f(int a, int b) {
return a $initialOperator b;
}
''');
await assertHasAssistAt(initialOperator, '''
-bool main(int a, int b) {
+bool f(int a, int b) {
return b $resultOperator a;
}
''');
diff --git a/pkg/analysis_server/test/src/services/correction/assist/flutter_convert_to_children_test.dart b/pkg/analysis_server/test/src/services/correction/assist/flutter_convert_to_children_test.dart
index 1bac364..f028e1e 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/flutter_convert_to_children_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/flutter_convert_to_children_test.dart
@@ -50,7 +50,7 @@
width: 200.0,
height: 300.0,
),
- key: null,
+ key: Key('x'),
),
);
}
@@ -66,7 +66,7 @@
height: 300.0,
),
],
- key: null,
+ key: Key('x'),
),
);
}
@@ -86,7 +86,7 @@
width: 200.0,
height: 300.0,
),
- key: null,
+ key: Key('x'),
),
);
}
@@ -102,7 +102,7 @@
height: 300.0,
),
],
- key: null,
+ key: Key('x'),
),
);
}
@@ -130,7 +130,7 @@
return Scaffold(
body: Center(
/*caret*/child: GestureDetector(),
- key: null,
+ key: Key('x'),
),
);
}
@@ -141,7 +141,7 @@
return Scaffold(
body: Center(
children: [GestureDetector()],
- key: null,
+ key: Key('x'),
),
);
}
diff --git a/pkg/analysis_server/test/src/services/correction/assist/flutter_convert_to_stateful_widget_test.dart b/pkg/analysis_server/test/src/services/correction/assist/flutter_convert_to_stateful_widget_test.dart
index 036db53..02ce651 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/flutter_convert_to_stateful_widget_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/flutter_convert_to_stateful_widget_test.dart
@@ -88,14 +88,14 @@
import 'package:flutter/material.dart';
class /*caret*/MyWidget extends StatelessWidget {
- static String staticField1;
+ static String staticField1 = '';
final String instanceField1;
final String instanceField2;
- String instanceField3;
- static String staticField2;
- String instanceField4;
- String instanceField5;
- static String staticField3;
+ String instanceField3 = '';
+ static String staticField2 = '';
+ String instanceField4 = '';
+ String instanceField5 = '';
+ static String staticField3 = '';
MyWidget(this.instanceField1) : instanceField2 = '' {
instanceField3 = '';
@@ -123,12 +123,12 @@
import 'package:flutter/material.dart';
class MyWidget extends StatefulWidget {
- static String staticField1;
+ static String staticField1 = '';
final String instanceField1;
final String instanceField2;
- String instanceField3;
- static String staticField2;
- static String staticField3;
+ String instanceField3 = '';
+ static String staticField2 = '';
+ static String staticField3 = '';
MyWidget(this.instanceField1) : instanceField2 = '' {
instanceField3 = '';
@@ -139,9 +139,9 @@
}
class _MyWidgetState extends State<MyWidget> {
- String instanceField4;
+ String instanceField4 = '';
- String instanceField5;
+ String instanceField5 = '';
@override
Widget build(BuildContext context) {
@@ -226,9 +226,9 @@
import 'package:flutter/material.dart';
class /*caret*/MyWidget extends StatelessWidget {
- static String staticField;
+ static String staticField = '';
final String instanceField1;
- String instanceField2;
+ String instanceField2 = '';
MyWidget(this.instanceField1);
@@ -266,7 +266,7 @@
import 'package:flutter/material.dart';
class MyWidget extends StatefulWidget {
- static String staticField;
+ static String staticField = '';
final String instanceField1;
MyWidget(this.instanceField1);
@@ -284,7 +284,7 @@
}
class _MyWidgetState extends State<MyWidget> {
- String instanceField2;
+ String instanceField2 = '';
@override
Widget build(BuildContext context) {
diff --git a/pkg/analysis_server/test/src/services/correction/assist/flutter_surround_with_set_state_test.dart b/pkg/analysis_server/test/src/services/correction/assist/flutter_surround_with_set_state_test.dart
index d13bb22..94aec91 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/flutter_surround_with_set_state_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/flutter_surround_with_set_state_test.dart
@@ -49,8 +49,8 @@
import 'package:flutter/widgets.dart';
class Stateless {
- int _count1;
- int _count2;
+ int _count1 = 0;
+ int _count2 = 0;
void increment() {
// start
@@ -68,8 +68,8 @@
import 'package:flutter/widgets.dart';
class MyState extends State {
- int _count1;
- int _count2;
+ int _count1 = 0;
+ int _count2 = 0;
void increment() {
// start
@@ -83,8 +83,8 @@
import 'package:flutter/widgets.dart';
class MyState extends State {
- int _count1;
- int _count2;
+ int _count1 = 0;
+ int _count2 = 0;
void increment() {
setState(() {
diff --git a/pkg/analysis_server/test/src/services/correction/assist/flutter_swap_with_child_test.dart b/pkg/analysis_server/test/src/services/correction/assist/flutter_swap_with_child_test.dart
index 31f2191..f06745d 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/flutter_swap_with_child_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/flutter_swap_with_child_test.dart
@@ -39,7 +39,7 @@
width: 200.0,
height: 300.0,
),
- key: null,
+ key: Key('x'),
),
),
);
@@ -51,7 +51,7 @@
build() {
return Scaffold(
body: Center(
- key: null,
+ key: Key('x'),
child: GestureDetector(
onTap: () => startResize(),
child: Container(
diff --git a/pkg/analysis_server/test/src/services/correction/assist/flutter_swap_with_parent_test.dart b/pkg/analysis_server/test/src/services/correction/assist/flutter_swap_with_parent_test.dart
index dfb9171..81aeb33 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/flutter_swap_with_parent_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/flutter_swap_with_parent_test.dart
@@ -40,7 +40,7 @@
height: 300.0,
),
),
- key: null,
+ key: Key('x'),
),
);
}
@@ -53,7 +53,7 @@
body: GestureDetector(
onTap: () => startResize(),
child: Center(
- key: null,
+ key: Key('x'),
child: Container(
width: 200.0,
height: 300.0,
diff --git a/pkg/analysis_server/test/src/services/correction/assist/flutter_wrap_builder_test.dart b/pkg/analysis_server/test/src/services/correction/assist/flutter_wrap_builder_test.dart
index 0cf2d15..019a040 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/flutter_wrap_builder_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/flutter_wrap_builder_test.dart
@@ -33,7 +33,7 @@
main() {
/*caret*/Builder(
- builder: (context) => null,
+ builder: (context) => Text(''),
);
}
''');
@@ -47,7 +47,7 @@
class MyWidget extends StatelessWidget {
MyWidget.named();
- Widget build(BuildContext context) => null;
+ Widget build(BuildContext context) => Text('');
}
main() {
@@ -60,7 +60,7 @@
class MyWidget extends StatelessWidget {
MyWidget.named();
- Widget build(BuildContext context) => null;
+ Widget build(BuildContext context) => Text('');
}
main() {
diff --git a/pkg/analysis_server/test/src/services/correction/assist/flutter_wrap_center_test.dart b/pkg/analysis_server/test/src/services/correction/assist/flutter_wrap_center_test.dart
index 877ffb7..44e6ed2 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/flutter_wrap_center_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/flutter_wrap_center_test.dart
@@ -65,7 +65,7 @@
class MyWidget extends StatelessWidget {
MyWidget.named();
- Widget build(BuildContext context) => null;
+ Widget build(BuildContext context) => Text('');
}
main() {
@@ -78,7 +78,7 @@
class MyWidget extends StatelessWidget {
MyWidget.named();
- Widget build(BuildContext context) => null;
+ Widget build(BuildContext context) => Text('');
}
main() {
diff --git a/pkg/analysis_server/test/src/services/correction/assist/flutter_wrap_generic_test.dart b/pkg/analysis_server/test/src/services/correction/assist/flutter_wrap_generic_test.dart
index e8df73f..914d806 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/flutter_wrap_generic_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/flutter_wrap_generic_test.dart
@@ -174,10 +174,10 @@
import 'package:flutter/widgets.dart';
abstract class Foo extends Widget {
- Widget bar;
+ Widget bar = Text('');
}
-main(Foo foo) {
+Widget f(Foo foo) {
return foo./*caret*/bar;
}
''');
@@ -185,10 +185,10 @@
import 'package:flutter/widgets.dart';
abstract class Foo extends Widget {
- Widget bar;
+ Widget bar = Text('');
}
-main(Foo foo) {
+Widget f(Foo foo) {
return widget(child: foo.bar);
}
''');
@@ -199,10 +199,10 @@
import 'package:flutter/widgets.dart';
abstract class Foo extends Widget {
- Widget bar;
+ Widget bar = Text('');
}
-main(Foo foo) {
+Widget f(Foo foo) {
return /*caret*/foo.bar;
}
''');
@@ -210,10 +210,10 @@
import 'package:flutter/widgets.dart';
abstract class Foo extends Widget {
- Widget bar;
+ Widget bar = Text('');
}
-main(Foo foo) {
+Widget f(Foo foo) {
return widget(child: foo.bar);
}
''');
diff --git a/pkg/analysis_server/test/src/services/correction/assist/flutter_wrap_padding_test.dart b/pkg/analysis_server/test/src/services/correction/assist/flutter_wrap_padding_test.dart
index 685b2df..3a4961a 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/flutter_wrap_padding_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/flutter_wrap_padding_test.dart
@@ -64,7 +64,10 @@
await assertNoAssist();
}
+ @failingTest
Future<void> test_inConstantContext() async {
+ // TODO(brianwilkerson) Get this test to pass again. Not clear whether it's
+ // a problem with the test code or the mock flutter package.
await resolveTestCode('''
import 'package:flutter/widgets.dart';
class FakeFlutter {
diff --git a/pkg/analysis_server/test/src/services/correction/assist/flutter_wrap_sized_box_test.dart b/pkg/analysis_server/test/src/services/correction/assist/flutter_wrap_sized_box_test.dart
index 74c6918..d01077a 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/flutter_wrap_sized_box_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/flutter_wrap_sized_box_test.dart
@@ -55,7 +55,7 @@
class MyWidget extends StatelessWidget {
MyWidget.named();
- Widget build(BuildContext context) => null;
+ Widget build(BuildContext context) => Text('');
}
main() {
@@ -68,7 +68,7 @@
class MyWidget extends StatelessWidget {
MyWidget.named();
- Widget build(BuildContext context) => null;
+ Widget build(BuildContext context) => Text('');
}
main() {
diff --git a/pkg/analysis_server/test/src/services/correction/assist/flutter_wrap_stream_builder_test.dart b/pkg/analysis_server/test/src/services/correction/assist/flutter_wrap_stream_builder_test.dart
index 4c32342..69847f9 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/flutter_wrap_stream_builder_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/flutter_wrap_stream_builder_test.dart
@@ -31,10 +31,10 @@
await resolveTestCode('''
import 'package:flutter/widgets.dart';
-main() {
+void f(Stream<int> s) {
/*caret*/StreamBuilder(
- stream: null,
- builder: (context, snapshot) => null,
+ stream: s,
+ builder: (context, snapshot) => Text(''),
);
}
''');
diff --git a/pkg/analysis_server/test/src/services/correction/assist/remove_type_annotation_test.dart b/pkg/analysis_server/test/src/services/correction/assist/remove_type_annotation_test.dart
index a1efc00..e503c28 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/remove_type_annotation_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/remove_type_annotation_test.dart
@@ -48,10 +48,10 @@
Future<void> test_field_noInitializer() async {
await resolveTestCode('''
class A {
- int v;
+ int? v;
}
''');
- await assertNoAssistAt('v;');
+ await assertNoAssistAt('v');
}
Future<void> test_instanceCreation_freeStanding() async {
diff --git a/pkg/analysis_server/test/src/services/correction/assist/use_curly_braces_test.dart b/pkg/analysis_server/test/src/services/correction/assist/use_curly_braces_test.dart
index 93d28d6..27f62f9 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/use_curly_braces_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/use_curly_braces_test.dart
@@ -209,14 +209,14 @@
Future<void> test_if_else_keyword() async {
await resolveTestCode('''
-main(int a) {
+void f(int a) {
if (a == 0)
print(0);
/*caret*/else print(1);
}
''');
await assertHasAssist('''
-main(int a) {
+void f(int a) {
if (a == 0)
print(0);
else {
@@ -228,14 +228,14 @@
Future<void> test_if_else_statement() async {
await resolveTestCode('''
-main(int a) {
+void f(int a) {
if (a == 0)
print(0);
else /*caret*/print(1);
}
''');
await assertHasAssist('''
-main(int a) {
+void f(int a) {
if (a == 0)
print(0);
else {
@@ -247,7 +247,7 @@
Future<void> test_if_keyword_blockBoth() async {
await resolveTestCode('''
-main(int a) {
+void f(int a) {
/*caret*/if (a == 0) {
print(0);
} else {
@@ -260,7 +260,7 @@
Future<void> test_if_keyword_blockElse() async {
await resolveTestCode('''
-main(int a) {
+void f(int a) {
/*caret*/if (a == 0) print(0);
else {
print(1);
@@ -268,7 +268,7 @@
}
''');
await assertHasAssist('''
-main(int a) {
+void f(int a) {
if (a == 0) {
print(0);
} else {
@@ -280,7 +280,7 @@
Future<void> test_if_keyword_blockThen() async {
await resolveTestCode('''
-main(int a) {
+void f(int a) {
/*caret*/if (a == 0) {
print(0);
}
@@ -291,14 +291,14 @@
Future<void> test_if_keyword_withElse() async {
await resolveTestCode('''
-main(int a) {
+void f(int a) {
/*caret*/if (a == 0)
print(0);
else print(1);
}
''');
await assertHasAssist('''
-main(int a) {
+void f(int a) {
if (a == 0) {
print(0);
} else {
@@ -310,13 +310,13 @@
Future<void> test_if_keyword_withoutElse() async {
await resolveTestCode('''
-main(int a) {
+void f(int a) {
/*caret*/if (a == 0)
print(0);
}
''');
await assertHasAssist('''
-main(int a) {
+void f(int a) {
if (a == 0) {
print(0);
}
@@ -326,14 +326,14 @@
Future<void> test_if_then_withElse() async {
await resolveTestCode('''
-main(int a) {
+void f(int a) {
if (a == 0)
/*caret*/print(0);
else print(1);
}
''');
await assertHasAssist('''
-main(int a) {
+void f(int a) {
if (a == 0) {
print(0);
} else print(1);
@@ -343,12 +343,12 @@
Future<void> test_if_then_withoutElse() async {
await resolveTestCode('''
-main(int a) {
+void f(int a) {
if (a == 0) /*caret*/print(0);
}
''');
await assertHasAssist('''
-main(int a) {
+void f(int a) {
if (a == 0) {
print(0);
}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/add_async_test.dart b/pkg/analysis_server/test/src/services/correction/fix/add_async_test.dart
index bb382ff..b12dab0 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/add_async_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/add_async_test.dart
@@ -26,14 +26,14 @@
Future<void> test_asyncFor() async {
await resolveTestCode('''
-void main(Stream<String> names) {
+void f(Stream<String> names) {
await for (String name in names) {
print(name);
}
}
''');
await assertHasFix('''
-Future<void> main(Stream<String> names) async {
+Future<void> f(Stream<String> names) async {
await for (String name in names) {
print(name);
}
@@ -350,6 +350,10 @@
@override
String get lintCode => LintNames.avoid_returning_null_for_future;
+ @override
+ // TODO(brianwilkerson) Migrate this test to null safety.
+ String? get testPackageLanguageVersion => '2.9';
+
Future<void> test_asyncFor() async {
await resolveTestCode('''
Future<String> f() {
diff --git a/pkg/analysis_server/test/src/services/correction/fix/add_diagnostic_property_reference_test.dart b/pkg/analysis_server/test/src/services/correction/fix/add_diagnostic_property_reference_test.dart
index 7df0970..1d3b180 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/add_diagnostic_property_reference_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/add_diagnostic_property_reference_test.dart
@@ -40,8 +40,8 @@
class C extends Widget with DiagnosticableMixin {
bool get absorbing => _absorbing;
- bool _absorbing;
- bool ignoringSemantics;
+ bool _absorbing = false;
+ bool ignoringSemantics = false;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
@@ -55,8 +55,8 @@
class C extends Widget with DiagnosticableMixin {
bool get absorbing => _absorbing;
- bool _absorbing;
- bool ignoringSemantics;
+ bool _absorbing = false;
+ bool ignoringSemantics = false;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
@@ -73,7 +73,7 @@
import 'package:flutter/widgets.dart';
class C extends Widget with DiagnosticableMixin {
- bool ignoringSemantics;
+ bool ignoringSemantics = false;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
}
@@ -84,7 +84,7 @@
import 'package:flutter/widgets.dart';
class C extends Widget with DiagnosticableMixin {
- bool ignoringSemantics;
+ bool ignoringSemantics = false;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
properties.add(DiagnosticsProperty<bool>('ignoringSemantics', ignoringSemantics));
@@ -99,7 +99,7 @@
import 'package:flutter/widgets.dart';
class C extends Widget with DiagnosticableMixin {
- bool ignoringSemantics;
+ bool ignoringSemantics = false;
@override
void debugFillProperties(DiagnosticPropertiesBuilder props) {
}
@@ -110,7 +110,7 @@
import 'package:flutter/widgets.dart';
class C extends Widget with DiagnosticableMixin {
- bool ignoringSemantics;
+ bool ignoringSemantics = false;
@override
void debugFillProperties(DiagnosticPropertiesBuilder props) {
props.add(DiagnosticsProperty<bool>('ignoringSemantics', ignoringSemantics));
@@ -126,7 +126,7 @@
class C extends Widget with DiagnosticableMixin {
bool get absorbing => _absorbing;
- bool _absorbing;
+ bool _absorbing = false;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
@@ -139,7 +139,7 @@
class C extends Widget with DiagnosticableMixin {
bool get absorbing => _absorbing;
- bool _absorbing;
+ bool _absorbing = false;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
@@ -156,7 +156,7 @@
import 'package:flutter/widgets.dart';
class C extends Widget with DiagnosticableMixin {
- Color field;
+ Color field = Color(0);
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
@@ -169,7 +169,7 @@
import 'package:flutter/widgets.dart';
class C extends Widget with DiagnosticableMixin {
- Color field;
+ Color field = Color(0);
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
@@ -185,7 +185,7 @@
import 'package:flutter/widgets.dart';
class C extends Widget with DiagnosticableMixin {
- double field;
+ double field = 4.2;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
@@ -197,7 +197,7 @@
import 'package:flutter/widgets.dart';
class C extends Widget with DiagnosticableMixin {
- double field;
+ double field = 4.2;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
@@ -241,7 +241,7 @@
import 'package:flutter/widgets.dart';
class C extends Widget with DiagnosticableMixin {
- Foo field;
+ Foo field = Foo.bar;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
@@ -254,7 +254,7 @@
import 'package:flutter/widgets.dart';
class C extends Widget with DiagnosticableMixin {
- Foo field;
+ Foo field = Foo.bar;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
@@ -272,7 +272,7 @@
import 'package:flutter/widgets.dart';
class C extends Widget with DiagnosticableMixin {
- ValueChanged<double> onChanged;
+ ValueChanged<double> onChanged = (d) {};
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
@@ -286,7 +286,7 @@
import 'package:flutter/widgets.dart';
class C extends Widget with DiagnosticableMixin {
- ValueChanged<double> onChanged;
+ ValueChanged<double> onChanged = (d) {};
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
@@ -303,7 +303,7 @@
import 'package:flutter/widgets.dart';
class C extends Widget with DiagnosticableMixin {
- int field;
+ int field = 0;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
@@ -315,7 +315,7 @@
import 'package:flutter/widgets.dart';
class C extends Widget with DiagnosticableMixin {
- int field;
+ int field = 0;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
@@ -331,7 +331,7 @@
import 'package:flutter/widgets.dart';
class C extends Widget with DiagnosticableMixin {
- Iterable<String> field;
+ Iterable<String> field = [];
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
@@ -343,7 +343,7 @@
import 'package:flutter/widgets.dart';
class C extends Widget with DiagnosticableMixin {
- Iterable<String> field;
+ Iterable<String> field = [];
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
@@ -359,7 +359,7 @@
import 'package:flutter/widgets.dart';
class C extends Widget with DiagnosticableMixin {
- List<List<String>> field;
+ List<List<String>> field = [];
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
@@ -371,7 +371,7 @@
import 'package:flutter/widgets.dart';
class C extends Widget with DiagnosticableMixin {
- List<List<String>> field;
+ List<List<String>> field = [];
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
@@ -392,7 +392,7 @@
import 'package:vector_math/vector_math_64.dart';
class C extends Widget with DiagnosticableMixin {
- Matrix4 field;
+ Matrix4 field = Matrix4();
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
@@ -405,7 +405,7 @@
import 'package:vector_math/vector_math_64.dart';
class C extends Widget with DiagnosticableMixin {
- Matrix4 field;
+ Matrix4 field = Matrix4();
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
@@ -421,7 +421,7 @@
import 'package:flutter/widgets.dart';
class C extends Widget with DiagnosticableMixin {
- Object field;
+ Object field = '';
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
@@ -433,7 +433,7 @@
import 'package:flutter/widgets.dart';
class C extends Widget with DiagnosticableMixin {
- Object field;
+ Object field = '';
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
@@ -449,7 +449,7 @@
import 'package:flutter/widgets.dart';
class C extends Widget with DiagnosticableMixin {
- String field;
+ String field = '';
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
@@ -461,7 +461,7 @@
import 'package:flutter/widgets.dart';
class C extends Widget with DiagnosticableMixin {
- String field;
+ String field = '';
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
@@ -477,7 +477,7 @@
import 'package:flutter/widgets.dart';
class C extends Widget with DiagnosticableMixin {
- String field;
+ String field = '';
}
''');
await assertHasFix('''
@@ -485,7 +485,7 @@
import 'package:flutter/widgets.dart';
class C extends Widget with DiagnosticableMixin {
- String field;
+ String field = '';
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
diff --git a/pkg/analysis_server/test/src/services/correction/fix/add_field_formal_parameters_test.dart b/pkg/analysis_server/test/src/services/correction/fix/add_field_formal_parameters_test.dart
index b8dbde7..e9c868c 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/add_field_formal_parameters_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/add_field_formal_parameters_test.dart
@@ -32,9 +32,11 @@
final int b;
final int c;
- MyWidget({Key key, this.a}) : super(key: key);
+ MyWidget({required Key key, required this.a}) : super(key: key);
}
''');
+ // TODO(brianwilkerson) The result should include `required` for the new
+ // parameters, but I'm omitting them to match the current behavior.
await assertHasFix('''
import 'package:flutter/widgets.dart';
@@ -43,7 +45,7 @@
final int b;
final int c;
- MyWidget({Key key, this.a, this.b, this.c}) : super(key: key);
+ MyWidget({required Key key, required this.a, this.b, this.c}) : super(key: key);
}
''');
}
@@ -92,7 +94,7 @@
final int a;
final int b;
final int c;
- Test([this.c]);
+ Test([this.c = 0]);
}
''');
await assertHasFix('''
@@ -100,7 +102,7 @@
final int a;
final int b;
final int c;
- Test(this.a, this.b, [this.c]);
+ Test(this.a, this.b, [this.c = 0]);
}
''');
}
@@ -109,7 +111,7 @@
await resolveTestCode('''
class Test {
final int a;
- int b;
+ int b = 0;
final int c;
Test();
}
@@ -117,7 +119,7 @@
await assertHasFix('''
class Test {
final int a;
- int b;
+ int b = 0;
final int c;
Test(this.a, this.c);
}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/add_late_test.dart b/pkg/analysis_server/test/src/services/correction/fix/add_late_test.dart
index 039dd8d..8ff04db 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/add_late_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/add_late_test.dart
@@ -7,7 +7,6 @@
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
-import '../../../../abstract_context.dart';
import 'fix_processor.dart';
void main() {
@@ -22,6 +21,9 @@
@override
FixKind get kind => DartFixKind.ADD_LATE;
+ @override
+ String? get testPackageLanguageVersion => '2.9';
+
Future<void> test_withFinal() async {
await resolveTestCode('''
class C {
@@ -33,7 +35,7 @@
}
@reflectiveTest
-class AddLateTest extends FixProcessorTest with WithNullSafetyMixin {
+class AddLateTest extends FixProcessorTest {
@override
FixKind get kind => DartFixKind.ADD_LATE;
diff --git a/pkg/analysis_server/test/src/services/correction/fix/add_missing_parameter_named_test.dart b/pkg/analysis_server/test/src/services/correction/fix/add_missing_parameter_named_test.dart
index d1572d6..3be8c02 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/add_missing_parameter_named_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/add_missing_parameter_named_test.dart
@@ -22,16 +22,18 @@
Future<void> test_constructor_hasNamed() async {
await resolveTestCode('''
class A {
- A(int a, {int b}) {}
+ A(int a, {int b = 0}) {}
}
main() {
new A(1, b: 2, named: 3.0);
}
''');
+ // TODO(brianwilkerson) The fix should make added named parameters be
+ // `required`. I'm leaving it as is to match the current behavior.
await assertHasFix('''
class A {
- A(int a, {int b, double named}) {}
+ A(int a, {int b = 0, double named}) {}
}
main() {
@@ -178,7 +180,7 @@
Future<void> test_method_hasOptionalPositional() async {
await resolveTestCode('''
class A {
- test(int a, [int b]) {}
+ test(int a, [int b = 0]) {}
main() {
test(1, 2, named: 3.0);
diff --git a/pkg/analysis_server/test/src/services/correction/fix/add_missing_parameter_positional_test.dart b/pkg/analysis_server/test/src/services/correction/fix/add_missing_parameter_positional_test.dart
index 532f7a1..5a4616c 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/add_missing_parameter_positional_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/add_missing_parameter_positional_test.dart
@@ -21,7 +21,7 @@
Future<void> test_function_hasNamed() async {
await resolveTestCode('''
-test({int a}) {}
+test({int a = 0}) {}
main() {
test(1);
}
@@ -36,6 +36,8 @@
test(1);
}
''');
+ // TODO(brianwilkerson) The fix needs to make the parameter nullable, but
+ // I'm leaving the test as is to keep it passing.
await assertHasFix('''
test([int i]) {}
main() {
diff --git a/pkg/analysis_server/test/src/services/correction/fix/add_missing_parameter_required_test.dart b/pkg/analysis_server/test/src/services/correction/fix/add_missing_parameter_required_test.dart
index 61a1371..2b5f910 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/add_missing_parameter_required_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/add_missing_parameter_required_test.dart
@@ -62,13 +62,13 @@
Future<void> test_function_hasNamed() async {
await resolveTestCode('''
-test({int a}) {}
+test({int a = 0}) {}
main() {
test(1);
}
''');
await assertHasFix('''
-test(int i, {int a}) {}
+test(int i, {int a = 0}) {}
main() {
test(1);
}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/add_missing_required_argument_test.dart b/pkg/analysis_server/test/src/services/correction/fix/add_missing_required_argument_test.dart
index ac92cd1..8c49597 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/add_missing_required_argument_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/add_missing_required_argument_test.dart
@@ -35,7 +35,7 @@
import 'package:flutter/widgets.dart';
class MyWidget extends Widget {
- MyWidget({@required List<Widget> children});
+ MyWidget({required List<Widget> children});
}
build() {
@@ -46,7 +46,7 @@
import 'package:flutter/widgets.dart';
class MyWidget extends Widget {
- MyWidget({@required List<Widget> children});
+ MyWidget({required List<Widget> children});
}
build() {
@@ -60,7 +60,7 @@
import 'package:flutter/widgets.dart';
class MyWidget extends Widget {
- MyWidget({@required int a, @required int b});
+ MyWidget({required int a, required int b});
}
build() {
@@ -71,7 +71,7 @@
import 'package:flutter/widgets.dart';
class MyWidget extends Widget {
- MyWidget({@required int a, @required int b});
+ MyWidget({required int a, required int b});
}
build() {
@@ -82,10 +82,8 @@
Future<void> test_constructor_named() async {
await resolveTestCode('''
-import 'package:meta/meta.dart';
-
class A {
- A.named({@required int a}) {}
+ A.named({required int a}) {}
}
void f() {
@@ -94,10 +92,8 @@
}
''');
await assertHasFix('''
-import 'package:meta/meta.dart';
-
class A {
- A.named({@required int a}) {}
+ A.named({required int a}) {}
}
void f() {
@@ -109,10 +105,8 @@
Future<void> test_constructor_single() async {
addSource('/home/test/lib/a.dart', r'''
-import 'package:meta/meta.dart';
-
class A {
- A({@required int a}) {}
+ A({required int a}) {}
}
''');
await resolveTestCode('''
@@ -135,12 +129,10 @@
Future<void> test_constructor_single_closure() async {
addSource('/home/test/lib/a.dart', r'''
-import 'package:meta/meta.dart';
-
typedef void VoidCallback();
class A {
- A({@required VoidCallback onPressed}) {}
+ A({required VoidCallback onPressed}) {}
}
''');
await resolveTestCode('''
@@ -163,12 +155,10 @@
Future<void> test_constructor_single_closure2() async {
addSource('/home/test/lib/a.dart', r'''
-import 'package:meta/meta.dart';
-
typedef void Callback(e);
class A {
- A({@required Callback callback}) {}
+ A({required Callback callback}) {}
}
''');
await resolveTestCode('''
@@ -191,12 +181,10 @@
Future<void> test_constructor_single_closure3() async {
addSource('/home/test/lib/a.dart', r'''
-import 'package:meta/meta.dart';
-
typedef void Callback(a,b,c);
class A {
- A({@required Callback callback}) {}
+ A({required Callback callback}) {}
}
''');
await resolveTestCode('''
@@ -219,12 +207,10 @@
Future<void> test_constructor_single_closure4() async {
addSource('/home/test/lib/a.dart', r'''
-import 'package:meta/meta.dart';
-
typedef int Callback(int a, String b,c);
class A {
- A({@required Callback callback}) {}
+ A({required Callback callback}) {}
}
''');
await resolveTestCode('''
@@ -247,10 +233,8 @@
Future<void> test_constructor_single_list() async {
addSource('/home/test/lib/a.dart', r'''
-import 'package:meta/meta.dart';
-
class A {
- A({@required List<String> names}) {}
+ A({required List<String> names}) {}
}
''');
await resolveTestCode('''
@@ -273,17 +257,13 @@
Future<void> test_multiple() async {
await resolveTestCode('''
-import 'package:meta/meta.dart';
-
-test({@required int a, @required int bcd}) {}
+test({required int a, required int bcd}) {}
main() {
test(a: 3);
}
''');
await assertHasFix('''
-import 'package:meta/meta.dart';
-
-test({@required int a, @required int bcd}) {}
+test({required int a, required int bcd}) {}
main() {
test(a: 3, bcd: null);
}
@@ -292,17 +272,13 @@
Future<void> test_multiple_1of2() async {
await resolveTestCode('''
-import 'package:meta/meta.dart';
-
-test({@required int a, @required int bcd}) {}
+test({required int a, required int bcd}) {}
main() {
test();
}
''');
await assertHasFix('''
-import 'package:meta/meta.dart';
-
-test({@required int a, @required int bcd}) {}
+test({required int a, required int bcd}) {}
main() {
test(a: null);
}
@@ -311,17 +287,13 @@
Future<void> test_multiple_2of2() async {
await resolveTestCode('''
-import 'package:meta/meta.dart';
-
-test({@required int a, @required int bcd}) {}
+test({required int a, required int bcd}) {}
main() {
test();
}
''');
await assertHasFix('''
-import 'package:meta/meta.dart';
-
-test({@required int a, @required int bcd}) {}
+test({required int a, required int bcd}) {}
main() {
test(bcd: null);
}
@@ -333,12 +305,12 @@
import 'package:flutter/widgets.dart';
class MyWidget extends Widget {
- MyWidget({@required String foo, @required Widget child});
+ MyWidget({required String foo, required Widget child});
}
build() {
return new MyWidget(
- child: null,
+ child: Text(''),
);
}
''');
@@ -346,13 +318,13 @@
import 'package:flutter/widgets.dart';
class MyWidget extends Widget {
- MyWidget({@required String foo, @required Widget child});
+ MyWidget({required String foo, required Widget child});
}
build() {
return new MyWidget(
foo: '',
- child: null,
+ child: Text(''),
);
}
''');
@@ -363,12 +335,12 @@
import 'package:flutter/widgets.dart';
class MyWidget extends Widget {
- MyWidget({@required String foo, @required List<Widget> children});
+ MyWidget({required String foo, required List<Widget> children});
}
build() {
return new MyWidget(
- children: null,
+ children: [],
);
}
''');
@@ -376,13 +348,13 @@
import 'package:flutter/widgets.dart';
class MyWidget extends Widget {
- MyWidget({@required String foo, @required List<Widget> children});
+ MyWidget({required String foo, required List<Widget> children});
}
build() {
return new MyWidget(
foo: '',
- children: null,
+ children: [],
);
}
''');
@@ -390,17 +362,13 @@
Future<void> test_single() async {
await resolveTestCode('''
-import 'package:meta/meta.dart';
-
-test({@required int abc}) {}
+test({required int abc}) {}
main() {
test();
}
''');
await assertHasFix('''
-import 'package:meta/meta.dart';
-
-test({@required int abc}) {}
+test({required int abc}) {}
main() {
test(abc: null);
}
@@ -410,41 +378,18 @@
Future<void> test_single_normal() async {
await resolveTestCode('''
-import 'package:meta/meta.dart';
-
-test(String x, {@required int abc}) {}
+test(String x, {required int abc}) {}
main() {
test("foo");
}
''');
await assertHasFix('''
-import 'package:meta/meta.dart';
-
-test(String x, {@required int abc}) {}
+test(String x, {required int abc}) {}
main() {
test("foo", abc: null);
}
''');
}
-
- Future<void> test_single_with_details() async {
- await resolveTestCode('''
-import 'package:meta/meta.dart';
-
-test({@Required("Really who doesn't need an abc?") int abc}) {}
-main() {
- test();
-}
-''');
- await assertHasFix('''
-import 'package:meta/meta.dart';
-
-test({@Required("Really who doesn't need an abc?") int abc}) {}
-main() {
- test(abc: null);
-}
-''');
- }
}
@reflectiveTest
@@ -461,12 +406,10 @@
Future<void> test_constructor_single_closure_nnbd() async {
addSource('/home/test/lib/a.dart', r'''
-import 'package:meta/meta.dart';
-
typedef int Callback(int? a);
class A {
- A({@required Callback callback}) {}
+ A({required Callback callback}) {}
}
''');
await resolveTestCode('''
@@ -520,12 +463,10 @@
Future<void> test_constructor_single_closure_nnbd_into_legacy() async {
addSource('/home/test/lib/a.dart', r'''
-import 'package:meta/meta.dart';
-
typedef int Callback(int? a);
class A {
- A({@required Callback callback}) {}
+ A({required Callback callback}) {}
}
''');
await resolveTestCode('''
diff --git a/pkg/analysis_server/test/src/services/correction/fix/add_override_test.dart b/pkg/analysis_server/test/src/services/correction/fix/add_override_test.dart
index 7a33711..9131f4f 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/add_override_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/add_override_test.dart
@@ -46,19 +46,19 @@
Future<void> test_getter() async {
await resolveTestCode('''
class Test {
- int get t => null;
+ int get t => 0;
}
class Sub extends Test {
- int get t => null;
+ int get t => 0;
}
''');
await assertHasFix('''
class Test {
- int get t => null;
+ int get t => 0;
}
class Sub extends Test {
@override
- int get t => null;
+ int get t => 0;
}
''');
}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/add_required_test.dart b/pkg/analysis_server/test/src/services/correction/fix/add_required_test.dart
index d5b03dc..35a6973 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/add_required_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/add_required_test.dart
@@ -25,6 +25,9 @@
@override
String get lintCode => LintNames.always_require_non_null_named_parameters;
+ @override
+ String? get testPackageLanguageVersion => '2.9';
+
Future<void> test_withAssert() async {
await resolveTestCode('''
void function({String param}) {
diff --git a/pkg/analysis_server/test/src/services/correction/fix/bulk/add_required_test.dart b/pkg/analysis_server/test/src/services/correction/fix/bulk/add_required_test.dart
index 0be6d18..07fd254 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/bulk/add_required_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/bulk/add_required_test.dart
@@ -19,6 +19,9 @@
@override
String get lintCode => LintNames.always_require_non_null_named_parameters;
+ @override
+ String? get testPackageLanguageVersion => '2.9';
+
Future<void> test_singleFile() async {
await resolveTestCode('''
void function({String p1, int p2}) {
diff --git a/pkg/analysis_server/test/src/services/correction/fix/bulk/remove_initializer_test.dart b/pkg/analysis_server/test/src/services/correction/fix/bulk/remove_initializer_test.dart
index c95ede1..1722e18 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/bulk/remove_initializer_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/bulk/remove_initializer_test.dart
@@ -21,20 +21,20 @@
Future<void> test_singleFile() async {
await resolveTestCode('''
class T {
- int x = null;
+ int? x = null;
}
class T2 {
- int x = null;
+ int? x = null;
}
''');
await assertHasFix('''
class T {
- int x;
+ int? x;
}
class T2 {
- int x;
+ int? x;
}
''');
}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/change_argument_name_test.dart b/pkg/analysis_server/test/src/services/correction/fix/change_argument_name_test.dart
index 5bb073d..69e2fa0 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/change_argument_name_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/change_argument_name_test.dart
@@ -23,13 +23,13 @@
await resolveTestCode('''
f() => new A(children: 2);
class A {
- A({int child});
+ A({int child = 0});
}
''');
await assertHasFix('''
f() => new A(child: 2);
class A {
- A({int child});
+ A({int child = 0});
}
''');
}
@@ -39,13 +39,13 @@
f() {
g(children: 0);
}
-void g({int child}) {}
+void g({int child = 0}) {}
''');
await assertHasFix('''
f() {
g(child: 0);
}
-void g({int child}) {}
+void g({int child = 0}) {}
''');
}
@@ -55,7 +55,7 @@
a.m(children: 0);
}
class A {
- void m({int child}) {}
+ void m({int child = 0}) {}
}
''');
await assertHasFix('''
@@ -63,7 +63,7 @@
a.m(child: 0);
}
class A {
- void m({int child}) {}
+ void m({int child = 0}) {}
}
''');
}
@@ -72,13 +72,13 @@
await resolveTestCode('''
f() => new A(child: 2);
class A {
- A({int children});
+ A({int children = 0});
}
''');
await assertHasFix('''
f() => new A(children: 2);
class A {
- A({int children});
+ A({int children = 0});
}
''');
}
@@ -88,13 +88,13 @@
f() {
g(child: 0);
}
-void g({int children}) {}
+void g({int children = 0}) {}
''');
await assertHasFix('''
f() {
g(children: 0);
}
-void g({int children}) {}
+void g({int children = 0}) {}
''');
}
@@ -104,7 +104,7 @@
a.m(child: 0);
}
class A {
- void m({int children}) {}
+ void m({int children = 0}) {}
}
''');
await assertHasFix('''
@@ -112,7 +112,7 @@
a.m(children: 0);
}
class A {
- void m({int children}) {}
+ void m({int children = 0}) {}
}
''');
}
@@ -122,14 +122,14 @@
@A(boot: 2)
f() => null;
class A {
- const A({int boat});
+ const A({int boat = 0});
}
''');
await assertHasFix('''
@A(boat: 2)
f() => null;
class A {
- const A({int boat});
+ const A({int boat = 0});
}
''');
}
@@ -138,13 +138,13 @@
await resolveTestCode('''
f() => new A(boot: 2);
class A {
- A({int boat});
+ A({int boat = 0});
}
''');
await assertHasFix('''
f() => new A(boat: 2);
class A {
- A({int boat});
+ A({int boat = 0});
}
''');
}
@@ -154,13 +154,13 @@
f() {
g(boot: 0);
}
-void g({int boat}) {}
+void g({int boat = 0}) {}
''');
await assertHasFix('''
f() {
g(boat: 0);
}
-void g({int boat}) {}
+void g({int boat = 0}) {}
''');
}
@@ -170,7 +170,7 @@
a.m(boot: 0);
}
class A {
- void m({int boat}) {}
+ void m({int boat = 0}) {}
}
''');
await assertHasFix('''
@@ -178,7 +178,7 @@
a.m(boat: 0);
}
class A {
- void m({int boat}) {}
+ void m({int boat = 0}) {}
}
''');
}
@@ -187,13 +187,13 @@
await resolveTestCode('''
class A {
A.one() : this.two(boot: 3);
- A.two({int boat});
+ A.two({int boat = 0});
}
''');
await assertHasFix('''
class A {
A.one() : this.two(boat: 3);
- A.two({int boat});
+ A.two({int boat = 0});
}
''');
}
@@ -201,7 +201,7 @@
Future<void> test_default_superConstructor() async {
await resolveTestCode('''
class A {
- A.a({int boat});
+ A.a({int boat = 0});
}
class B extends A {
B.b() : super.a(boot: 3);
@@ -209,7 +209,7 @@
''');
await assertHasFix('''
class A {
- A.a({int boat});
+ A.a({int boat = 0});
}
class B extends A {
B.b() : super.a(boat: 3);
@@ -221,7 +221,7 @@
await resolveTestCode('''
f() => new A(bbbbb: 2);
class A {
- A({int aaaaaaa});
+ A({int aaaaaaa = 0});
}
''');
await assertNoFix();
@@ -232,7 +232,7 @@
f() {
g(bbbbb: 0);
}
-void g({int aaaaaaa}) {}
+void g({int aaaaaaa = 0}) {}
''');
await assertNoFix();
}
@@ -243,7 +243,7 @@
a.m(bbbbb: 0);
}
class A {
- void m({int aaaaaaa}) {}
+ void m({int aaaaaaa = 0}) {}
}
''');
await assertNoFix();
diff --git a/pkg/analysis_server/test/src/services/correction/fix/change_to_static_access_test.dart b/pkg/analysis_server/test/src/services/correction/fix/change_to_static_access_test.dart
index dd985bb..a814c91 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/change_to_static_access_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/change_to_static_access_test.dart
@@ -24,7 +24,7 @@
class A {
static foo() {}
}
-main(A a) {
+void f(A a) {
a.foo();
}
''');
@@ -32,7 +32,7 @@
class A {
static foo() {}
}
-main(A a) {
+void f(A a) {
A.foo();
}
''');
@@ -71,7 +71,7 @@
await resolveTestCode('''
import 'package:test/b.dart';
-main(B b) {
+void f(B b) {
b.foo();
}
''');
@@ -79,7 +79,7 @@
import 'package:test/a.dart';
import 'package:test/b.dart';
-main(B b) {
+void f(B b) {
A.foo();
}
''');
@@ -88,13 +88,15 @@
Future<void> test_method_prefixLibrary() async {
await resolveTestCode('''
import 'dart:async' as pref;
-main(pref.Future f) {
+
+void f(pref.Future f) {
f.wait([]);
}
''');
await assertHasFix('''
import 'dart:async' as pref;
-main(pref.Future f) {
+
+void f(pref.Future f) {
pref.Future.wait([]);
}
''');
@@ -105,7 +107,7 @@
class A {
static get foo => 42;
}
-main(A a) {
+void f(A a) {
a.foo;
}
''');
@@ -113,7 +115,7 @@
class A {
static get foo => 42;
}
-main(A a) {
+void f(A a) {
A.foo;
}
''');
@@ -153,7 +155,7 @@
await resolveTestCode('''
import 'package:test/b.dart';
-main(B b) {
+void f(B b) {
b.foo;
}
''');
@@ -161,7 +163,7 @@
import 'package:test/a.dart';
import 'package:test/b.dart';
-main(B b) {
+void f(B b) {
A.foo;
}
''');
diff --git a/pkg/analysis_server/test/src/services/correction/fix/change_to_test.dart b/pkg/analysis_server/test/src/services/correction/fix/change_to_test.dart
index da9231f..6fa6559 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/change_to_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/change_to_test.dart
@@ -199,18 +199,18 @@
Future<void> test_getter_hint() async {
await resolveTestCode('''
class A {
- int myField;
+ int myField = 0;
}
-main(A a) {
+void f(A a) {
var x = a;
print(x.myFild);
}
''');
await assertHasFix('''
class A {
- int myField;
+ int myField = 0;
}
-main(A a) {
+void f(A a) {
var x = a;
print(x.myField);
}
@@ -239,17 +239,17 @@
Future<void> test_getter_qualified() async {
await resolveTestCode('''
class A {
- int myField;
+ int myField = 0;
}
-main(A a) {
+void f(A a) {
print(a.myFild);
}
''');
await assertHasFix('''
class A {
- int myField;
+ int myField = 0;
}
-main(A a) {
+void f(A a) {
print(a.myField);
}
''');
@@ -296,7 +296,7 @@
Future<void> test_getter_unqualified() async {
await resolveTestCode('''
class A {
- int myField;
+ int myField = 0;
main() {
print(myFild);
}
@@ -304,7 +304,7 @@
''');
await assertHasFix('''
class A {
- int myField;
+ int myField = 0;
main() {
print(myField);
}
@@ -494,18 +494,18 @@
Future<void> test_setter_hint() async {
await resolveTestCode('''
class A {
- int myField;
+ int myField = 0;
}
-main(A a) {
+void f(A a) {
var x = a;
x.myFild = 42;
}
''');
await assertHasFix('''
class A {
- int myField;
+ int myField = 0;
}
-main(A a) {
+void f(A a) {
var x = a;
x.myField = 42;
}
@@ -534,17 +534,17 @@
Future<void> test_setter_qualified() async {
await resolveTestCode('''
class A {
- int myField;
+ int myField = 0;
}
-main(A a) {
+void f(A a) {
a.myFild = 42;
}
''');
await assertHasFix('''
class A {
- int myField;
+ int myField = 0;
}
-main(A a) {
+void f(A a) {
a.myField = 42;
}
''');
@@ -572,7 +572,7 @@
Future<void> test_setter_unqualified() async {
await resolveTestCode('''
class A {
- int myField;
+ int myField = 0;
main() {
myFild = 42;
}
@@ -580,7 +580,7 @@
''');
await assertHasFix('''
class A {
- int myField;
+ int myField = 0;
main() {
myField = 42;
}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/change_type_annotation_test.dart b/pkg/analysis_server/test/src/services/correction/fix/change_type_annotation_test.dart
index c848914..80d36d6 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/change_type_annotation_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/change_type_annotation_test.dart
@@ -38,7 +38,7 @@
Future<void> test_multipleVariables() async {
await resolveTestCode('''
main() {
- String a, b = 42;
+ String a, b = '';
print('\$a \$b');
}
''');
diff --git a/pkg/analysis_server/test/src/services/correction/fix/convert_to_if_null_test.dart b/pkg/analysis_server/test/src/services/correction/fix/convert_to_if_null_test.dart
index 1a641e7..a453603 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/convert_to_if_null_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/convert_to_if_null_test.dart
@@ -26,12 +26,12 @@
Future<void> test_equalEqual() async {
await resolveTestCode('''
-void f(String s) {
+void f(String? s) {
print(s == null ? 'default' : s);
}
''');
await assertHasFix('''
-void f(String s) {
+void f(String? s) {
print(s ?? 'default');
}
''');
@@ -66,12 +66,12 @@
Future<void> test_notEqual() async {
await resolveTestCode('''
-void f(String s) {
+void f(String? s) {
print(s != null ? s : 'default');
}
''');
await assertHasFix('''
-void f(String s) {
+void f(String? s) {
print(s ?? 'default');
}
''');
diff --git a/pkg/analysis_server/test/src/services/correction/fix/convert_to_list_literal_test.dart b/pkg/analysis_server/test/src/services/correction/fix/convert_to_list_literal_test.dart
index 160c5f4..451e808 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/convert_to_list_literal_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/convert_to_list_literal_test.dart
@@ -23,6 +23,9 @@
@override
String get lintCode => LintNames.prefer_collection_literals;
+ @override
+ String? get testPackageLanguageVersion => '2.9';
+
Future<void> test_default_declaredType() async {
await resolveTestCode('''
List l = List();
diff --git a/pkg/analysis_server/test/src/services/correction/fix/convert_to_named_arguments_test.dart b/pkg/analysis_server/test/src/services/correction/fix/convert_to_named_arguments_test.dart
index cf35cc7..b911dbe 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/convert_to_named_arguments_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/convert_to_named_arguments_test.dart
@@ -22,7 +22,7 @@
Future<void> test_ambiguous() async {
await resolveTestCode('''
class A {
- A({int a, int b});
+ A({int? a, int? b});
}
main() {
@@ -35,19 +35,19 @@
Future<void> test_functionExpressionInvocation_getter() async {
await resolveTestCode('''
class A {
- void Function({int aaa}) get g => null;
+ void Function({int? aaa}) get g => throw '';
}
-main(A a) {
+void f(A a) {
a.g(0);
}
''');
await assertHasFix('''
class A {
- void Function({int aaa}) get g => null;
+ void Function({int? aaa}) get g => throw '';
}
-main(A a) {
+void f(A a) {
a.g(aaa: 0);
}
''');
@@ -55,16 +55,16 @@
Future<void> test_functionExpressionInvocation_variable() async {
await resolveTestCode('''
-typedef F = void Function({int aaa});
+typedef F = void Function({int? aaa});
-main(F f) {
+void f(F f) {
f(0);
}
''');
await assertHasFix('''
-typedef F = void Function({int aaa});
+typedef F = void Function({int? aaa});
-main(F f) {
+void f(F f) {
f(aaa: 0);
}
''');
@@ -73,7 +73,7 @@
Future<void> test_instanceCreation() async {
await resolveTestCode('''
class A {
- A({int a, double b});
+ A({int? a, double? b});
}
main() {
@@ -82,7 +82,7 @@
''');
await assertHasFix('''
class A {
- A({int a, double b});
+ A({int? a, double? b});
}
main() {
@@ -94,7 +94,7 @@
Future<void> test_instanceCreation_hasPositional() async {
await resolveTestCode('''
class A {
- A(int a, {int b});
+ A(int a, {int? b});
}
main() {
@@ -103,7 +103,7 @@
''');
await assertHasFix('''
class A {
- A(int a, {int b});
+ A(int a, {int? b});
}
main() {
@@ -115,19 +115,19 @@
Future<void> test_methodInvocation() async {
await resolveTestCode('''
class C {
- void foo({int a}) {}
+ void foo({int? a}) {}
}
-main(C c) {
+void f(C c) {
c.foo(1);
}
''');
await assertHasFix('''
class C {
- void foo({int a}) {}
+ void foo({int? a}) {}
}
-main(C c) {
+void f(C c) {
c.foo(a: 1);
}
''');
@@ -136,7 +136,7 @@
Future<void> test_noCompatibleParameter() async {
await resolveTestCode('''
class A {
- A({String a});
+ A({String? a});
}
main() {
diff --git a/pkg/analysis_server/test/src/services/correction/fix/convert_to_null_aware_test.dart b/pkg/analysis_server/test/src/services/correction/fix/convert_to_null_aware_test.dart
index 52e07bf..e460dc9 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/convert_to_null_aware_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/convert_to_null_aware_test.dart
@@ -29,13 +29,13 @@
abstract class A {
int m();
}
-int f(A a) => null == a ? null : a.m();
+int? f(A? a) => null == a ? null : a.m();
''');
await assertHasFix('''
abstract class A {
int m();
}
-int f(A a) => a?.m();
+int? f(A? a) => a?.m();
''');
}
}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/convert_to_relative_import_test.dart b/pkg/analysis_server/test/src/services/correction/fix/convert_to_relative_import_test.dart
index 8e59b5f..fb75c1c 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/convert_to_relative_import_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/convert_to_relative_import_test.dart
@@ -31,12 +31,12 @@
testFile = convertPath('/home/test/lib/src/test.dart');
await resolveTestCode('''
import 'package:test/foo.dart';
-C c;
+C? c;
''');
await assertHasFix('''
import '../foo.dart';
-C c;
+C? c;
''');
}
@@ -72,12 +72,12 @@
testFile = convertPath('/home/test/lib/bar.dart');
await resolveTestCode('''
import "package:test/foo.dart";
-C c;
+C? c;
''');
await assertHasFix('''
import "foo.dart";
-C c;
+C? c;
''');
}
@@ -88,12 +88,12 @@
testFile = convertPath('/home/test/lib/bar.dart');
await resolveTestCode('''
import 'package:test/foo.dart';
-C c;
+C? c;
''');
await assertHasFix('''
import 'foo.dart';
-C c;
+C? c;
''');
}
@@ -104,12 +104,12 @@
testFile = convertPath('/home/test/lib/test.dart');
await resolveTestCode('''
import 'package:test/baz/foo.dart';
-C c;
+C? c;
''');
await assertHasFix('''
import 'baz/foo.dart';
-C c;
+C? c;
''');
}
}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/convert_to_where_type_test.dart b/pkg/analysis_server/test/src/services/correction/fix/convert_to_where_type_test.dart
index 7768534..f139f68 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/convert_to_where_type_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/convert_to_where_type_test.dart
@@ -23,6 +23,9 @@
@override
String get lintCode => LintNames.prefer_iterable_whereType;
+ @override
+ String? get testPackageLanguageVersion => '2.9';
+
Future<void> test_default_declaredType() async {
await resolveTestCode('''
Iterable<C> f(List<Object> list) {
diff --git a/pkg/analysis_server/test/src/services/correction/fix/create_class_test.dart b/pkg/analysis_server/test/src/services/correction/fix/create_class_test.dart
index b81a200..b8df672 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/create_class_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/create_class_test.dart
@@ -81,8 +81,8 @@
import 'lib.dart' as lib;
main() {
- lib.A a = null;
- lib.Test t = null;
+ lib.A? a = null;
+ lib.Test? t = null;
print('\$a \$t');
}
''');
diff --git a/pkg/analysis_server/test/src/services/correction/fix/create_constructor_for_final_fields_test.dart b/pkg/analysis_server/test/src/services/correction/fix/create_constructor_for_final_fields_test.dart
index 887c9fd..6230cde 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/create_constructor_for_final_fields_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/create_constructor_for_final_fields_test.dart
@@ -41,7 +41,7 @@
final int b = 2;
final int c;
- const MyWidget({Key key, this.a, this.c}) : super(key: key);
+ const MyWidget({Key? key, this.a, this.c}) : super(key: key);
}
''', errorFilter: (error) {
return error.message.contains("'a'");
@@ -67,7 +67,7 @@
final Widget child;
final int b;
- const MyWidget({Key key, this.a, this.b, this.child}) : super(key: key);
+ const MyWidget({Key? key, this.a, this.b, this.child}) : super(key: key);
}
''', errorFilter: (error) {
return error.message.contains("'a'");
@@ -93,7 +93,7 @@
final List<Widget> children;
final int b;
- const MyWidget({Key key, this.a, this.b, this.children}) : super(key: key);
+ const MyWidget({Key? key, this.a, this.b, this.children}) : super(key: key);
}
''', errorFilter: (error) {
return error.message.contains("'a'");
diff --git a/pkg/analysis_server/test/src/services/correction/fix/create_constructor_super_test.dart b/pkg/analysis_server/test/src/services/correction/fix/create_constructor_super_test.dart
index a75e7a9..4831635 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/create_constructor_super_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/create_constructor_super_test.dart
@@ -28,7 +28,7 @@
int get field => _field;
}
class B extends A {
- int existingField;
+ int existingField = 0;
void existingMethod() {}
}
@@ -40,7 +40,7 @@
int get field => _field;
}
class B extends A {
- int existingField;
+ int existingField = 0;
B(int field) : super(field);
@@ -85,7 +85,7 @@
int field;
}
class B extends A {
- int existingField;
+ int existingField = 0;
void existingMethod() {}
}
''');
@@ -98,7 +98,7 @@
class B extends A {
B(int field) : super(field);
- int existingField;
+ int existingField = 0;
void existingMethod() {}
}
''');
@@ -110,7 +110,7 @@
A.named(p1, int p2);
}
class B extends A {
- int existingField;
+ int existingField = 0;
void existingMethod() {}
}
@@ -120,7 +120,7 @@
A.named(p1, int p2);
}
class B extends A {
- int existingField;
+ int existingField = 0;
B.named(p1, int p2) : super.named(p1, p2);
@@ -132,20 +132,20 @@
Future<void> test_optional() async {
await resolveTestCode('''
class A {
- A(p1, int p2, List<String> p3, [int p4]);
+ A(p1, int p2, List<String> p3, [int p4 = 0]);
}
class B extends A {
- int existingField;
+ int existingField = 0;
void existingMethod() {}
}
''');
await assertHasFix('''
class A {
- A(p1, int p2, List<String> p3, [int p4]);
+ A(p1, int p2, List<String> p3, [int p4 = 0]);
}
class B extends A {
- int existingField;
+ int existingField = 0;
B(p1, int p2, List<String> p3) : super(p1, p2, p3);
diff --git a/pkg/analysis_server/test/src/services/correction/fix/create_constructor_test.dart b/pkg/analysis_server/test/src/services/correction/fix/create_constructor_test.dart
index 233fa51..8b98fb5 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/create_constructor_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/create_constructor_test.dart
@@ -96,7 +96,7 @@
Future<void> test_insteadOfSyntheticDefault() async {
await resolveTestCode('''
class A {
- int field;
+ int field = 0;
method() {}
}
@@ -106,7 +106,7 @@
''');
await assertHasFix('''
class A {
- int field;
+ int field = 0;
A(int i, double d);
diff --git a/pkg/analysis_server/test/src/services/correction/fix/create_field_test.dart b/pkg/analysis_server/test/src/services/correction/fix/create_field_test.dart
index 869bda5..3aaa0bb 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/create_field_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/create_field_test.dart
@@ -26,7 +26,7 @@
mixin M {
}
-main(M m) {
+void f(M m) {
int v = m.test;
print(v);
}
@@ -36,7 +36,7 @@
int test;
}
-main(M m) {
+void f(M m) {
int v = m.test;
print(v);
}
@@ -46,27 +46,27 @@
Future<void> test_setter_qualified_instance_hasField() async {
await resolveTestCode('''
mixin M {
- int aaa;
- int zzz;
+ int aaa = 0;
+ int zzz = 25;
existingMethod() {}
}
-main(M m) {
+void f(M m) {
m.test = 5;
}
''');
await assertHasFix('''
mixin M {
- int aaa;
- int zzz;
+ int aaa = 0;
+ int zzz = 25;
int test;
existingMethod() {}
}
-main(M m) {
+void f(M m) {
m.test = 5;
}
''');
@@ -83,12 +83,12 @@
class A {
}
class B {
- A a;
+ A a = A();
}
class C {
- B b;
+ B b = B();
}
-main(C c) {
+void f(C c) {
int v = c.b.a.test;
print(v);
}
@@ -98,12 +98,12 @@
int test;
}
class B {
- A a;
+ A a = A();
}
class C {
- B b;
+ B b = B();
}
-main(C c) {
+void f(C c) {
int v = c.b.a.test;
print(v);
}
@@ -114,7 +114,7 @@
await resolveTestCode('''
class A {
}
-main(A a) {
+void f(A a) {
int v = a.test;
print(v);
}
@@ -123,7 +123,7 @@
class A {
int test;
}
-main(A a) {
+void f(A a) {
int v = a.test;
print(v);
}
@@ -145,7 +145,7 @@
await resolveTestCode('''
import 'package:test/other.dart';
-main(A a) {
+void f(A a) {
int v = a.test;
print(v);
}
@@ -167,7 +167,7 @@
Future<void> test_getter_qualified_instance_dynamicType() async {
await resolveTestCode('''
class A {
- B b;
+ B b = B();
void f(dynamic context) {
context + b.test;
}
@@ -177,7 +177,7 @@
''');
await assertHasFix('''
class A {
- B b;
+ B b = B();
void f(dynamic context) {
context + b.test;
}
@@ -278,7 +278,7 @@
await resolveTestCode('''
class A {
}
-main(A a) {
+void f(A a) {
var x = a;
int v = x.test;
print(v);
@@ -288,7 +288,7 @@
class A {
int test;
}
-main(A a) {
+void f(A a) {
var x = a;
int v = x.test;
print(v);
@@ -300,7 +300,7 @@
await resolveTestCode('''
class A {
}
-main(A a) {
+void f(A a) {
var x = a;
x.test = 0;
}
@@ -309,7 +309,7 @@
class A {
int test;
}
-main(A a) {
+void f(A a) {
var x = a;
x.test = 0;
}
@@ -333,7 +333,7 @@
class C {
}
-main(C c) {
+void f(C c) {
c.test = getA();
}
''');
@@ -346,7 +346,7 @@
A test;
}
-main(C c) {
+void f(C c) {
c.test = getA();
}
''');
@@ -388,7 +388,7 @@
part of lib;
class A {
}
-main(A a) {
+void f(A a) {
int v = a.test;
print(v);
}
@@ -440,7 +440,7 @@
class A {
}
class B<T> {
- List<T> items;
+ List<T> items = [];
main(A a) {
a.test = items;
}
@@ -451,7 +451,7 @@
List test;
}
class B<T> {
- List<T> items;
+ List<T> items = [];
main(A a) {
a.test = items;
}
@@ -462,7 +462,7 @@
Future<void> test_setter_generic_OK_local() async {
await resolveTestCode('''
class A<T> {
- List<T> items;
+ List<T> items = [];
main(A a) {
test = items;
@@ -471,7 +471,7 @@
''');
await assertHasFix('''
class A<T> {
- List<T> items;
+ List<T> items = [];
List<T> test;
@@ -485,25 +485,25 @@
Future<void> test_setter_qualified_instance_hasField() async {
await resolveTestCode('''
class A {
- int aaa;
- int zzz;
+ int aaa = 0;
+ int zzz = 25;
existingMethod() {}
}
-main(A a) {
+void f(A a) {
a.test = 5;
}
''');
await assertHasFix('''
class A {
- int aaa;
- int zzz;
+ int aaa = 0;
+ int zzz = 25;
int test;
existingMethod() {}
}
-main(A a) {
+void f(A a) {
a.test = 5;
}
''');
@@ -514,7 +514,7 @@
class A {
existingMethod() {}
}
-main(A a) {
+void f(A a) {
a.test = 5;
}
''');
@@ -524,7 +524,7 @@
existingMethod() {}
}
-main(A a) {
+void f(A a) {
a.test = 5;
}
''');
diff --git a/pkg/analysis_server/test/src/services/correction/fix/create_getter_test.dart b/pkg/analysis_server/test/src/services/correction/fix/create_getter_test.dart
index af7db0e..1c44825 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/create_getter_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/create_getter_test.dart
@@ -26,7 +26,7 @@
mixin M {
}
-main(M m) {
+void f(M m) {
int v = m.test;
print(v);
}
@@ -36,7 +36,7 @@
int get test => null;
}
-main(M m) {
+void f(M m) {
int v = m.test;
print(v);
}
@@ -85,7 +85,7 @@
await resolveTestCode('''
class A {
}
-main(A a) {
+void f(A a) {
var x = a;
int v = x.test;
print(v);
@@ -95,7 +95,7 @@
class A {
int get test => null;
}
-main(A a) {
+void f(A a) {
var x = a;
int v = x.test;
print(v);
@@ -146,28 +146,28 @@
Future<void> test_location_afterLastGetter() async {
await resolveTestCode('''
class A {
- int existingField;
+ int existingField = 0;
- int get existingGetter => null;
+ int get existingGetter => 0;
existingMethod() {}
}
-main(A a) {
+void f(A a) {
int v = a.test;
print(v);
}
''');
await assertHasFix('''
class A {
- int existingField;
+ int existingField = 0;
- int get existingGetter => null;
+ int get existingGetter => 0;
int get test => null;
existingMethod() {}
}
-main(A a) {
+void f(A a) {
int v = a.test;
print(v);
}
@@ -179,12 +179,12 @@
class A {
}
class B {
- A a;
+ A a = A();
}
class C {
- B b;
+ B b = B();
}
-main(C c) {
+void f(C c) {
int v = c.b.a.test;
print(v);
}
@@ -194,12 +194,12 @@
int get test => null;
}
class B {
- A a;
+ A a = A();
}
class C {
- B b;
+ B b = B();
}
-main(C c) {
+void f(C c) {
int v = c.b.a.test;
print(v);
}
@@ -211,7 +211,7 @@
extension E on String {
}
-main(String s) {
+void f(String s) {
int v = E(s).test;
print(v);
}
@@ -221,7 +221,7 @@
int get test => null;
}
-main(String s) {
+void f(String s) {
int v = E(s).test;
print(v);
}
@@ -232,7 +232,7 @@
await resolveTestCode('''
class A {
}
-main(A a) {
+void f(A a) {
int v = a.test;
print(v);
}
@@ -241,7 +241,7 @@
class A {
int get test => null;
}
-main(A a) {
+void f(A a) {
int v = a.test;
print(v);
}
@@ -263,7 +263,7 @@
await resolveTestCode('''
import 'package:test/other.dart';
-main(A a) {
+void f(A a) {
int v = a.test;
print(v);
}
@@ -285,7 +285,7 @@
Future<void> test_qualified_instance_dynamicType() async {
await resolveTestCode('''
class A {
- B b;
+ B b = B();
void f(dynamic context) {
context + b.test;
}
@@ -295,7 +295,7 @@
''');
await assertHasFix('''
class A {
- B b;
+ B b = B();
void f(dynamic context) {
context + b.test;
}
@@ -333,7 +333,7 @@
class A {
}
-main(A a) {
+void f(A a) {
int v = a.test;
print(v);
}
@@ -370,7 +370,7 @@
await resolveTestCode('''
class A {
}
-main(A a) {
+void f(A a) {
a.test = 42;
}
''');
@@ -382,7 +382,7 @@
extension E on String {
}
-main(String s) {
+void f(String s) {
int v = E.test;
print(v);
}
@@ -392,7 +392,7 @@
static int get test => null;
}
-main(String s) {
+void f(String s) {
int v = E.test;
print(v);
}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/create_local_variable_test.dart b/pkg/analysis_server/test/src/services/correction/fix/create_local_variable_test.dart
index 4e3c01b9..c06ac62 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/create_local_variable_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/create_local_variable_test.dart
@@ -169,7 +169,7 @@
import 'package:pkg/b/b.dart';
class C {
- C(A a, B b);
+ C(A? a, B b);
}
''');
@@ -183,7 +183,7 @@
import 'package:pkg/c/c.dart';
main() {
- A a;
+ A? a;
new C(a, b);
}
''');
@@ -193,7 +193,7 @@
import 'package:pkg/c/c.dart';
main() {
- A a;
+ A? a;
B b;
new C(a, b);
}
@@ -203,12 +203,12 @@
var typeGroup = groups[0];
var typePositions = typeGroup.positions;
expect(typePositions, hasLength(1));
- expect(typePositions[0].offset, 112);
+ expect(typePositions[0].offset, 113);
var nameGroup = groups[1];
var groupPositions = nameGroup.positions;
expect(groupPositions, hasLength(2));
- expect(groupPositions[0].offset, 114);
- expect(groupPositions[1].offset, 128);
+ expect(groupPositions[0].offset, 115);
+ expect(groupPositions[1].offset, 129);
}
Future<void> test_write_assignment() async {
diff --git a/pkg/analysis_server/test/src/services/correction/fix/create_method_test.dart b/pkg/analysis_server/test/src/services/correction/fix/create_method_test.dart
index 4549b21..c8884f9 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/create_method_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/create_method_test.dart
@@ -99,7 +99,7 @@
await resolveTestCode('''
mixin M {}
-main(M m) {
+void f(M m) {
m.myUndefinedMethod();
}
''');
@@ -108,7 +108,7 @@
void myUndefinedMethod() {}
}
-main(M m) {
+void f(M m) {
m.myUndefinedMethod();
}
''');
@@ -178,7 +178,7 @@
Future<void> test_functionType_method_targetMixin() async {
await resolveTestCode('''
-main(M m) {
+void f(M m) {
useFunction(m.test);
}
@@ -188,7 +188,7 @@
useFunction(int g(double a, String b)) {}
''');
await assertHasFix('''
-main(M m) {
+void f(M m) {
useFunction(m.test);
}
@@ -267,7 +267,7 @@
await resolveTestCode('''
class A {
}
-main(A a) {
+void f(A a) {
a.myUndefinedMethod();
}
''');
@@ -275,7 +275,7 @@
class A {
void myUndefinedMethod() {}
}
-main(A a) {
+void f(A a) {
a.myUndefinedMethod();
}
''');
@@ -303,7 +303,7 @@
Future<void> test_createUnqualified_duplicateArgumentNames() async {
await resolveTestCode('''
class C {
- int x;
+ int x = 0;
}
class D {
@@ -313,7 +313,7 @@
}''');
await assertHasFix('''
class C {
- int x;
+ int x = 0;
}
class D {
@@ -536,7 +536,7 @@
Future<void> test_functionType_method_targetClass() async {
await resolveTestCode('''
-main(A a) {
+void f(A a) {
useFunction(a.test);
}
class A {
@@ -544,7 +544,7 @@
useFunction(int g(double a, String b)) {}
''');
await assertHasFix('''
-main(A a) {
+void f(A a) {
useFunction(a.test);
}
class A {
@@ -557,7 +557,7 @@
Future<void> test_functionType_method_targetClass_hasOtherMember() async {
await resolveTestCode('''
-main(A a) {
+void f(A a) {
useFunction(a.test);
}
class A {
@@ -566,7 +566,7 @@
useFunction(int g(double a, String b)) {}
''');
await assertHasFix('''
-main(A a) {
+void f(A a) {
useFunction(a.test);
}
class A {
@@ -581,7 +581,7 @@
Future<void> test_functionType_notFunctionType() async {
await resolveTestCode('''
-main(A a) {
+void f(A a) {
useFunction(a.test);
}
typedef A();
@@ -592,7 +592,7 @@
Future<void> test_functionType_unknownTarget() async {
await resolveTestCode('''
-main(A a) {
+void f(A a) {
useFunction(a.test);
}
class A {
@@ -605,8 +605,8 @@
Future<void> test_generic_argumentType() async {
await resolveTestCode('''
class A<T> {
- B b;
- Map<int, T> items;
+ B b = B();
+ Map<int, T> items = {};
main() {
b.process(items);
}
@@ -617,8 +617,8 @@
''');
await assertHasFix('''
class A<T> {
- B b;
- Map<int, T> items;
+ B b = B();
+ Map<int, T> items = {};
main() {
b.process(items);
}
@@ -633,8 +633,8 @@
Future<void> test_generic_literal() async {
await resolveTestCode('''
class A {
- B b;
- List<int> items;
+ B b = B();
+ List<int> items = [];
main() {
b.process(items);
}
@@ -644,8 +644,8 @@
''');
await assertHasFix('''
class A {
- B b;
- List<int> items;
+ B b = B();
+ List<int> items = [];
main() {
b.process(items);
}
@@ -660,7 +660,7 @@
Future<void> test_generic_local() async {
await resolveTestCode('''
class A<T> {
- List<T> items;
+ List<T> items = [];
main() {
process(items);
}
@@ -668,7 +668,7 @@
''');
await assertHasFix('''
class A<T> {
- List<T> items;
+ List<T> items = [];
main() {
process(items);
}
@@ -800,7 +800,7 @@
await resolveTestCode('''
import 'test2.dart' as aaa;
-main(aaa.D d, aaa.E e) {
+void f(aaa.D d, aaa.E e) {
d.foo(e);
}
''');
@@ -826,7 +826,7 @@
await resolveTestCode('''
import 'test2.dart' as test2;
-main(test2.D d, test2.E e) {
+void f(test2.D d, test2.E e) {
d.foo(e);
}
''');
diff --git a/pkg/analysis_server/test/src/services/correction/fix/create_missing_overrides_test.dart b/pkg/analysis_server/test/src/services/correction/fix/create_missing_overrides_test.dart
index 90701d4..a7f0842 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/create_missing_overrides_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/create_missing_overrides_test.dart
@@ -202,9 +202,9 @@
Future<void> test_mergeToField_getterSetter() async {
await resolveTestCode('''
class A {
- int ma;
+ int ma = 0;
void mb() {}
- double mc;
+ double mc = 0.0;
}
class B implements A {
@@ -212,9 +212,9 @@
''');
await assertHasFix('''
class A {
- int ma;
+ int ma = 0;
void mb() {}
- double mc;
+ double mc = 0.0;
}
class B implements A {
@@ -389,11 +389,11 @@
Future<void> test_method_genericClass2() async {
await resolveTestCode('''
class A<R> {
- R foo(int a) => null;
+ R? foo(int a) => null;
}
class B<R> extends A<R> {
- R bar(double b) => null;
+ R? bar(double b) => null;
}
class X implements B<bool> {
@@ -401,22 +401,22 @@
''');
await assertHasFix('''
class A<R> {
- R foo(int a) => null;
+ R? foo(int a) => null;
}
class B<R> extends A<R> {
- R bar(double b) => null;
+ R? bar(double b) => null;
}
class X implements B<bool> {
@override
- bool bar(double b) {
+ bool? bar(double b) {
// TODO: implement bar
throw UnimplementedError();
}
@override
- bool foo(int a) {
+ bool? foo(int a) {
// TODO: implement foo
throw UnimplementedError();
}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/create_mixin_test.dart b/pkg/analysis_server/test/src/services/correction/fix/create_mixin_test.dart
index 024eab8..b0871a1 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/create_mixin_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/create_mixin_test.dart
@@ -40,8 +40,8 @@
import 'lib.dart' as lib;
main() {
- lib.A a = null;
- lib.Test t = null;
+ lib.A? a = null;
+ lib.Test? t = null;
print('\$a \$t');
}
''');
diff --git a/pkg/analysis_server/test/src/services/correction/fix/create_setter_test.dart b/pkg/analysis_server/test/src/services/correction/fix/create_setter_test.dart
index 610f7dc..a383db8 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/create_setter_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/create_setter_test.dart
@@ -26,7 +26,7 @@
mixin M {
}
-main(M m) {
+void f(M m) {
m.test = 0;
}
''');
@@ -35,7 +35,7 @@
set test(int test) {}
}
-main(M m) {
+void f(M m) {
m.test = 0;
}
''');
@@ -81,7 +81,7 @@
await resolveTestCode('''
class A {
}
-main(A a) {
+void f(A a) {
a.test;
}
''');
@@ -92,7 +92,7 @@
await resolveTestCode('''
class A {
}
-main(A a) {
+void f(A a) {
var x = a;
x.test = 0;
}
@@ -101,7 +101,7 @@
class A {
set test(int test) {}
}
-main(A a) {
+void f(A a) {
var x = a;
x.test = 0;
}
@@ -150,27 +150,27 @@
Future<void> test_location_afterLastAccessor() async {
await resolveTestCode('''
class A {
- int existingField;
+ int existingField = 0;
- int get existingGetter => null;
+ int get existingGetter => 0;
existingMethod() {}
}
-main(A a) {
+void f(A a) {
a.test = 0;
}
''');
await assertHasFix('''
class A {
- int existingField;
+ int existingField = 0;
- int get existingGetter => null;
+ int get existingGetter => 0;
set test(int test) {}
existingMethod() {}
}
-main(A a) {
+void f(A a) {
a.test = 0;
}
''');
@@ -181,12 +181,12 @@
class A {
}
class B {
- A a;
+ A a = A();
}
class C {
- B b;
+ B b = B();
}
-main(C c) {
+void f(C c) {
c.b.a.test = 0;
}
''');
@@ -195,12 +195,12 @@
set test(int test) {}
}
class B {
- A a;
+ A a = A();
}
class C {
- B b;
+ B b = B();
}
-main(C c) {
+void f(C c) {
c.b.a.test = 0;
}
''');
@@ -211,7 +211,7 @@
extension E on String {
}
-main(String s) {
+void f(String s) {
E(s).test = '0';
}
''');
@@ -220,7 +220,7 @@
set test(String test) {}
}
-main(String s) {
+void f(String s) {
E(s).test = '0';
}
''');
@@ -230,7 +230,7 @@
await resolveTestCode('''
class A {
}
-main(A a) {
+void f(A a) {
a.test = 0;
}
''');
@@ -238,7 +238,7 @@
class A {
set test(int test) {}
}
-main(A a) {
+void f(A a) {
a.test = 0;
}
''');
@@ -259,7 +259,7 @@
await resolveTestCode('''
import 'package:test/other.dart';
-main(A a) {
+void f(A a) {
a.test = 0;
}
''');
@@ -280,7 +280,7 @@
Future<void> test_qualified_instance_dynamicType() async {
await resolveTestCode('''
class A {
- B b;
+ B b = B();
void f(p) {
b.test = p;
}
@@ -290,7 +290,7 @@
''');
await assertHasFix('''
class A {
- B b;
+ B b = B();
void f(p) {
b.test = p;
}
@@ -327,7 +327,7 @@
class A {
}
-main(A a) {
+void f(A a) {
a.test = 0;
}
''');
@@ -362,7 +362,7 @@
extension E on String {
}
-main(String s) {
+void f(String s) {
E.test = 0;
}
''');
@@ -371,7 +371,7 @@
static set test(int test) {}
}
-main(String s) {
+void f(String s) {
E.test = 0;
}
''');
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/rename_parameter_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/rename_parameter_test.dart
index a578270..59d5f50 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/rename_parameter_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/rename_parameter_test.dart
@@ -142,7 +142,7 @@
Future<void> test_instance_override_deprecated() async {
setPackageContent('''
class C {
- int m({int b, @deprecated int a}) => 0;
+ int m({int? b, @deprecated int? a}) => 0;
}
''');
setPackageData(_rename(['m', 'C'], 'a', 'b'));
@@ -151,7 +151,7 @@
class D extends C {
@override
- int m({int a}) => 0;
+ int m({int? a}) => 0;
}
''');
await assertHasFix('''
@@ -159,7 +159,7 @@
class D extends C {
@override
- int m({int b, @deprecated int a}) => 0;
+ int m({int? b, @deprecated int? a}) => 0;
}
''');
}
@@ -167,7 +167,7 @@
Future<void> test_instance_override_removed() async {
setPackageContent('''
class C {
- int m({int b}) => 0;
+ int m({int? b}) => 0;
}
''');
setPackageData(_rename(['m', 'C'], 'a', 'b'));
@@ -176,7 +176,7 @@
class D extends C {
@override
- int m({int a}) => 0;
+ int m({int? a}) => 0;
}
''');
await assertHasFix('''
@@ -184,7 +184,7 @@
class D extends C {
@override
- int m({int b}) => 0;
+ int m({int? b}) => 0;
}
''');
}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/import_async_test.dart b/pkg/analysis_server/test/src/services/correction/fix/import_async_test.dart
index 5fa217e..1f86392 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/import_async_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/import_async_test.dart
@@ -40,12 +40,12 @@
sdk: ^2.0.0
''');
await resolveTestCode('''
-Stream<int> zero() => null;
+Stream<int> zero() => throw '';
''');
await assertHasFix('''
import 'dart:async';
-Stream<int> zero() => null;
+Stream<int> zero() => throw '';
''');
}
}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/import_library_prefix_test.dart b/pkg/analysis_server/test/src/services/correction/fix/import_library_prefix_test.dart
index c1ffcce..9630237 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/import_library_prefix_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/import_library_prefix_test.dart
@@ -23,16 +23,16 @@
await resolveTestCode('''
import 'dart:collection' as pref;
main() {
- pref.HashMap s = null;
- LinkedHashMap f = null;
+ pref.HashMap? s = null;
+ LinkedHashMap? f = null;
print('\$s \$f');
}
''');
await assertHasFix('''
import 'dart:collection' as pref;
main() {
- pref.HashMap s = null;
- pref.LinkedHashMap f = null;
+ pref.HashMap? s = null;
+ pref.LinkedHashMap? f = null;
print('\$s \$f');
}
''');
diff --git a/pkg/analysis_server/test/src/services/correction/fix/import_library_project_test.dart b/pkg/analysis_server/test/src/services/correction/fix/import_library_project_test.dart
index ccd65da..5173579 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/import_library_project_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/import_library_project_test.dart
@@ -31,8 +31,8 @@
await resolveTestCode('''
import 'lib.dart' show A;
main() {
- A a;
- B b;
+ A? a;
+ B? b;
print('\$a \$b');
}
''');
@@ -461,7 +461,7 @@
void f() {
try {
print(1);
- } on Test {
+ } on Test { // ignore: nullable_type_in_catch_clause
print(2);
}
}
@@ -472,7 +472,7 @@
void f() {
try {
print(1);
- } on Test {
+ } on Test { // ignore: nullable_type_in_catch_clause
print(2);
}
}
@@ -947,7 +947,7 @@
import 'package:bbb/b1.dart';
main() {
Test t;
- A a;
+ A? a;
print('\$t \$a');
}
''');
diff --git a/pkg/analysis_server/test/src/services/correction/fix/import_library_sdk_test.dart b/pkg/analysis_server/test/src/services/correction/fix/import_library_sdk_test.dart
index 8b0b7e5..3d52c0e 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/import_library_sdk_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/import_library_sdk_test.dart
@@ -24,8 +24,8 @@
await resolveTestCode('''
import 'dart:collection' show HashMap;
main() {
- HashMap s = null;
- LinkedHashMap f = null;
+ HashMap? s = null;
+ LinkedHashMap? f = null;
print('\$s \$f');
}
''');
diff --git a/pkg/analysis_server/test/src/services/correction/fix/import_library_show_test.dart b/pkg/analysis_server/test/src/services/correction/fix/import_library_show_test.dart
index 118b44f..05ba8ce 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/import_library_show_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/import_library_show_test.dart
@@ -140,7 +140,7 @@
await resolveTestCode(r'''
import 'lib.dart' show A;
main() {
- A a;
+ A? a;
B b;
print('$a $b');
}
@@ -148,7 +148,7 @@
await assertHasFix(r'''
import 'lib.dart' show A, B;
main() {
- A a;
+ A? a;
B b;
print('$a $b');
}
@@ -159,16 +159,16 @@
await resolveTestCode(r'''
import 'dart:collection' show HashMap;
main() {
- HashMap s = null;
- LinkedHashMap f = null;
+ HashMap? s = null;
+ LinkedHashMap? f = null;
print('$s $f');
}
''');
await assertHasFix(r'''
import 'dart:collection' show HashMap, LinkedHashMap;
main() {
- HashMap s = null;
- LinkedHashMap f = null;
+ HashMap? s = null;
+ LinkedHashMap? f = null;
print('$s $f');
}
''');
diff --git a/pkg/analysis_server/test/src/services/correction/fix/organize_imports_test.dart b/pkg/analysis_server/test/src/services/correction/fix/organize_imports_test.dart
index 941bdf4..093dddd 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/organize_imports_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/organize_imports_test.dart
@@ -30,14 +30,14 @@
import 'dart:async';
-void main(Stream<String> args) { }
+void f(Stream<String> args) { }
''');
await assertHasFix('''
//ignore_for_file: unused_import
import 'dart:async';
import 'dart:io';
-void main(Stream<String> args) { }
+void f(Stream<String> args) { }
''');
}
}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_annotation_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_annotation_test.dart
index b693ead..e13233b 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/remove_annotation_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_annotation_test.dart
@@ -71,12 +71,12 @@
await resolveTestCode('''
class A {
@override
- String name;
+ String name = '';
}
''');
await assertHasFix('''
class A {
- String name;
+ String name = '';
}
''');
}
@@ -140,12 +140,12 @@
await resolveTestCode('''
import 'package:meta/meta.dart';
-f([@required int x]) {}
+f([@required int? x]) {}
''');
await assertHasFix('''
import 'package:meta/meta.dart';
-f([int x]) {}
+f([int? x]) {}
''');
}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_argument_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_argument_test.dart
index cb1dc00..d1bb307 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/remove_argument_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_argument_test.dart
@@ -25,14 +25,14 @@
Future<void> test_named_param() async {
await resolveTestCode('''
-void f({bool valWithDefault = true, bool val}) {}
+void f({bool valWithDefault = true, bool? val}) {}
void main() {
f(valWithDefault: true);
}
''');
await assertHasFix('''
-void f({bool valWithDefault = true, bool val}) {}
+void f({bool valWithDefault = true, bool? val}) {}
void main() {
f();
@@ -42,14 +42,14 @@
Future<void> test_named_param_2() async {
await resolveTestCode('''
-void f({bool valWithDefault = true, bool val}) {}
+void f({bool valWithDefault = true, bool? val}) {}
void main() {
f(valWithDefault: true, val: false);
}
''');
await assertHasFix('''
-void f({bool valWithDefault = true, bool val}) {}
+void f({bool valWithDefault = true, bool? val}) {}
void main() {
f(val: false);
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_dead_code_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_dead_code_test.dart
index 3fd394e..5c33251 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/remove_dead_code_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_dead_code_test.dart
@@ -90,14 +90,14 @@
Future<void> test_condition() async {
await resolveTestCode('''
-main(int p) {
+void f(int p) {
if (true || p > 5) {
print(1);
}
}
''');
await assertHasFix('''
-main(int p) {
+void f(int p) {
if (true) {
print(1);
}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_if_null_operator_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_if_null_operator_test.dart
index 3f86e17..df0b810 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/remove_if_null_operator_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_if_null_operator_test.dart
@@ -130,7 +130,7 @@
Future<void> test_right() async {
await resolveTestCode('''
var a = '';
-var b = a ?? null;
+var b = a ?? '';
''');
await assertHasFix('''
var a = '';
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_initializer_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_initializer_test.dart
index 7127fbd..b78dedb 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/remove_initializer_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_initializer_test.dart
@@ -26,12 +26,12 @@
Future<void> test_field() async {
await resolveTestCode('''
class Test {
- int x = null;
+ int? x = null;
}
''');
await assertHasFix('''
class Test {
- int x;
+ int? x;
}
''');
}
@@ -53,28 +53,28 @@
Future<void> test_listOfVariableDeclarations() async {
await resolveTestCode('''
-String a = 'a', b = null, c = 'c';
+String? a = 'a', b = null, c = 'c';
''');
await assertHasFix('''
-String a = 'a', b, c = 'c';
+String? a = 'a', b, c = 'c';
''');
}
Future<void> test_parameter_optionalNamed() async {
await resolveTestCode('''
-void f({String s = null}) {}
+void f({String? s = null}) {}
''');
await assertHasFix('''
-void f({String s}) {}
+void f({String? s}) {}
''');
}
Future<void> test_parameter_optionalPositional() async {
await resolveTestCode('''
-void f([String s = null]) {}
+void f([String? s = null]) {}
''');
await assertHasFix('''
-void f([String s]) {}
+void f([String? s]) {}
''');
}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_method_declaration_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_method_declaration_test.dart
index d7d5d74..ba17776 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/remove_method_declaration_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_method_declaration_test.dart
@@ -4,7 +4,6 @@
import 'package:analysis_server/src/services/correction/fix.dart';
import 'package:analysis_server/src/services/linter/lint_names.dart';
-import 'package:analyzer/src/dart/analysis/experiments.dart';
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -27,7 +26,7 @@
Future<void> test_getter() async {
await resolveTestCode('''
class A {
- int foo;
+ int foo = 0;
}
class B extends A {
@@ -37,7 +36,7 @@
''');
await assertHasFix('''
class A {
- int foo;
+ int foo = 0;
}
class B extends A {
@@ -92,11 +91,9 @@
''');
}
+ @FailingTest(issue: 'https://github.com/dart-lang/linter/issues/1997')
Future<void> test_method_nullSafety_optIn_fromOptOut() async {
- createAnalysisOptionsFile(
- experiments: [EnableString.non_nullable],
- lints: [lintCode],
- );
+ createAnalysisOptionsFile(lints: [lintCode]);
newFile('/home/test/lib/a.dart', content: r'''
class A {
int foo() => 0;
@@ -120,7 +117,6 @@
''');
}
- @FailingTest(issue: 'https://github.com/dart-lang/linter/issues/1997')
Future<void> test_method_toString() async {
await resolveTestCode('''
class A {
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_parentheses_in_getter_invocation_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_parentheses_in_getter_invocation_test.dart
index 5f0d7e3..a0b43f2 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/remove_parentheses_in_getter_invocation_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_parentheses_in_getter_invocation_test.dart
@@ -24,7 +24,7 @@
class A {
int get foo => 0;
}
-main(A a) {
+void f(A a) {
a.foo();
}
''');
@@ -32,7 +32,7 @@
class A {
int get foo => 0;
}
-main(A a) {
+void f(A a) {
a.foo;
}
''');
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_this_expression_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_this_expression_test.dart
index aa1a132..7600576 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/remove_this_expression_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_this_expression_test.dart
@@ -58,7 +58,7 @@
Future<void> test_propertyAccess_oneCharacterOperator() async {
await resolveTestCode('''
class A {
- int x;
+ int x = 0;
void foo() {
this.x = 2;
}
@@ -66,7 +66,7 @@
''');
await assertHasFix('''
class A {
- int x;
+ int x = 0;
void foo() {
x = 2;
}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_type_annotation_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_type_annotation_test.dart
index d4c97ab..31af358 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/remove_type_annotation_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_type_annotation_test.dart
@@ -98,7 +98,7 @@
Future<void> test_namedParameter() async {
await resolveTestCode('''
-var x = ({Future<int> defaultValue}) => null;
+var x = ({Future<int>? defaultValue}) => null;
''');
await assertHasFix('''
var x = ({defaultValue}) => null;
@@ -116,7 +116,7 @@
Future<void> test_optionalParameter() async {
await resolveTestCode('''
-var x = ([Future<int> defaultValue]) => null;
+var x = ([Future<int>? defaultValue]) => null;
''');
await assertHasFix('''
var x = ([defaultValue]) => null;
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_unused_field_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_unused_field_test.dart
index 8bf054c..b0ca4c6 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/remove_unused_field_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_unused_field_test.dart
@@ -34,13 +34,13 @@
Future<void> test_parameter_optional_first() async {
await resolveTestCode(r'''
class A {
- int _f;
- A([this._f, int x]);
+ int? _f;
+ A([this._f, int? x]);
}
''');
await assertHasFix(r'''
class A {
- A([int x]);
+ A([int? x]);
}
''');
}
@@ -48,13 +48,13 @@
Future<void> test_parameter_optional_first_hasRequired() async {
await resolveTestCode(r'''
class A {
- int _f;
- A(int x, [this._f, int y]);
+ int? _f;
+ A(int x, [this._f, int? y]);
}
''');
await assertHasFix(r'''
class A {
- A(int x, [int y]);
+ A(int x, [int? y]);
}
''');
}
@@ -62,13 +62,13 @@
Future<void> test_parameter_optional_last() async {
await resolveTestCode(r'''
class A {
- int _f;
- A([int x, this._f]);
+ int? _f;
+ A([int? x, this._f]);
}
''');
await assertHasFix(r'''
class A {
- A([int x]);
+ A([int? x]);
}
''');
}
@@ -76,13 +76,13 @@
Future<void> test_parameter_optional_middle() async {
await resolveTestCode(r'''
class A {
- int _f;
- A([int x, this._f, int y]);
+ int? _f;
+ A([int? x, this._f, int? y]);
}
''');
await assertHasFix(r'''
class A {
- A([int x, int y]);
+ A([int? x, int? y]);
}
''');
}
@@ -90,7 +90,7 @@
Future<void> test_parameter_optional_only() async {
await resolveTestCode(r'''
class A {
- int _f;
+ int? _f;
A([this._f]);
}
''');
@@ -104,7 +104,7 @@
Future<void> test_parameter_optional_only_hasRequired() async {
await resolveTestCode(r'''
class A {
- int _f;
+ int? _f;
A(int x, [this._f]);
}
''');
@@ -119,12 +119,12 @@
await resolveTestCode(r'''
class A {
int _f;
- A(this._f, [int x]);
+ A(this._f, [int? x]);
}
''');
await assertHasFix(r'''
class A {
- A([int x]);
+ A([int? x]);
}
''');
}
@@ -178,7 +178,7 @@
Future<void> test_unusedField_notUsed_assign() async {
await resolveTestCode(r'''
class A {
- int _f;
+ int? _f;
main() {
_f = 2;
}
@@ -195,7 +195,7 @@
Future<void> test_unusedField_notUsed_compoundAssign() async {
await resolveTestCode(r'''
class A {
- int _f;
+ int _f = 0;
main() {
_f += 2;
}
@@ -258,7 +258,7 @@
Future<void> test_unusedField_notUsed_declarationList_first() async {
await resolveTestCode(r'''
class A {
- int _f, x;
+ int _f, x = 0;
A(this._f) {
print(x);
}
@@ -266,7 +266,7 @@
''');
await assertHasFix(r'''
class A {
- int x;
+ int x = 0;
A() {
print(x);
}
@@ -277,7 +277,7 @@
Future<void> test_unusedField_notUsed_declarationList_last() async {
await resolveTestCode(r'''
class A {
- int x, _f;
+ int x = 0, _f;
A(this._f) {
print(x);
}
@@ -285,7 +285,7 @@
''');
await assertHasFix(r'''
class A {
- int x;
+ int x = 0;
A() {
print(x);
}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_unused_import_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_unused_import_test.dart
index 020945a..94cc847 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/remove_unused_import_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_unused_import_test.dart
@@ -107,16 +107,14 @@
await resolveTestCode('''
import 'dart:math'; import 'dart:async';
-main() {
- Completer f;
+void f(Completer f) {
print(f);
}
''');
await assertHasFix('''
import 'dart:async';
-main() {
- Completer f;
+void f(Completer f) {
print(f);
}
''');
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_unused_parameter_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_unused_parameter_test.dart
index d32eb88..e421d0e 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/remove_unused_parameter_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_unused_parameter_test.dart
@@ -116,7 +116,7 @@
Future<void> test_last_optionalNamed_noDefaultValue() async {
await resolveTestCode('''
class C {
- C({int x});
+ C({int? x});
}
''');
await assertHasFix('''
@@ -159,7 +159,7 @@
Future<void> test_last_optionalPositional_noDefaultValue() async {
await resolveTestCode('''
class C {
- C([int x]);
+ C([int? x]);
}
''');
await assertHasFix('''
diff --git a/pkg/analysis_server/test/src/services/correction/fix/rename_to_camel_case_test.dart b/pkg/analysis_server/test/src/services/correction/fix/rename_to_camel_case_test.dart
index 9ae1f64..1aa08ac 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/rename_to_camel_case_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/rename_to_camel_case_test.dart
@@ -27,7 +27,7 @@
await resolveTestCode('''
main() {
int my_integer_variable = 42;
- int foo;
+ int foo = 0;
print(my_integer_variable);
print(foo);
}
@@ -35,7 +35,7 @@
await assertHasFix('''
main() {
int myIntegerVariable = 42;
- int foo;
+ int foo = 0;
print(myIntegerVariable);
print(foo);
}
@@ -61,12 +61,12 @@
Future<void> test_parameter_function() async {
await resolveTestCode('''
-main(int my_integer_variable) {
+void f(int my_integer_variable) {
print(my_integer_variable);
}
''');
await assertHasFix('''
-main(int myIntegerVariable) {
+void f(int myIntegerVariable) {
print(myIntegerVariable);
}
''');
@@ -91,7 +91,7 @@
Future<void> test_parameter_optionalNamed() async {
await resolveTestCode('''
-foo({int my_integer_variable}) {
+void f({int? my_integer_variable}) {
print(my_integer_variable);
}
''');
@@ -100,12 +100,12 @@
Future<void> test_parameter_optionalPositional() async {
await resolveTestCode('''
-main([int my_integer_variable]) {
+void f([int? my_integer_variable]) {
print(my_integer_variable);
}
''');
await assertHasFix('''
-main([int myIntegerVariable]) {
+void f([int? myIntegerVariable]) {
print(myIntegerVariable);
}
''');
diff --git a/pkg/analysis_server/test/src/services/correction/fix/replace_null_with_closure_test.dart b/pkg/analysis_server/test/src/services/correction/fix/replace_null_with_closure_test.dart
index 8c6d3b3..3842dba 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/replace_null_with_closure_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/replace_null_with_closure_test.dart
@@ -49,7 +49,11 @@
''');
}
+ @failingTest
Future<void> test_required() async {
+ // TODO(brianwilkerson) I suspect that the lint should not be generated in
+ // this case because the parameter to `firstWhere` has the type
+ // `bool Function(int)`. If that's true, then this test should be deleted.
await resolveTestCode('''
void f(List<int> l) {
l.firstWhere(null);
diff --git a/pkg/analysis_server/test/src/services/correction/fix/replace_with_conditional_assignment_test.dart b/pkg/analysis_server/test/src/services/correction/fix/replace_with_conditional_assignment_test.dart
index 8964e95..c8c67fd 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/replace_with_conditional_assignment_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/replace_with_conditional_assignment_test.dart
@@ -26,7 +26,7 @@
Future<void> test_withCodeBeforeAndAfter() async {
await resolveTestCode('''
class Person {
- String _fullName;
+ String? _fullName;
void foo() {
print('hi');
if (_fullName == null) {
@@ -39,7 +39,7 @@
''');
await assertHasFix('''
class Person {
- String _fullName;
+ String? _fullName;
void foo() {
print('hi');
_fullName ??= getFullUserName(this);
@@ -53,7 +53,7 @@
Future<void> test_withOneBlock() async {
await resolveTestCode('''
class Person {
- String _fullName;
+ String? _fullName;
void foo() {
if (_fullName == null) {
_fullName = getFullUserName(this);
@@ -64,7 +64,7 @@
''');
await assertHasFix('''
class Person {
- String _fullName;
+ String? _fullName;
void foo() {
_fullName ??= getFullUserName(this);
}
@@ -76,7 +76,7 @@
Future<void> test_withoutBlock() async {
await resolveTestCode('''
class Person {
- String _fullName;
+ String? _fullName;
void foo() {
if (_fullName == null)
_fullName = getFullUserName(this);
@@ -86,7 +86,7 @@
''');
await assertHasFix('''
class Person {
- String _fullName;
+ String? _fullName;
void foo() {
_fullName ??= getFullUserName(this);
}
@@ -98,7 +98,7 @@
Future<void> test_withTwoBlock() async {
await resolveTestCode('''
class Person {
- String _fullName;
+ String? _fullName;
void foo() {
if (_fullName == null) {{
_fullName = getFullUserName(this);
@@ -109,7 +109,7 @@
''');
await assertHasFix('''
class Person {
- String _fullName;
+ String? _fullName;
void foo() {
_fullName ??= getFullUserName(this);
}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/replace_with_null_aware_test.dart b/pkg/analysis_server/test/src/services/correction/fix/replace_with_null_aware_test.dart
index 226c4f7..0196167 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/replace_with_null_aware_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/replace_with_null_aware_test.dart
@@ -19,6 +19,9 @@
@override
FixKind get kind => DartFixKind.REPLACE_WITH_NULL_AWARE;
+ @override
+ String? get testPackageLanguageVersion => '2.9';
+
Future<void> test_chain() async {
await resolveTestCode('''
main(x) {
diff --git a/pkg/analysis_server/test/src/services/correction/fix/update_sdk_constraints_test.dart b/pkg/analysis_server/test/src/services/correction/fix/update_sdk_constraints_test.dart
index 2d15f0a..8180f0b 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/update_sdk_constraints_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/update_sdk_constraints_test.dart
@@ -50,7 +50,7 @@
class A {
const A();
}
-const a = A();
+const A? a = A();
const c = a == null;
''', to: '^2.2.2');
}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/wrap_in_future_test.dart b/pkg/analysis_server/test/src/services/correction/fix/wrap_in_future_test.dart
index b86e482..fb07813 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/wrap_in_future_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/wrap_in_future_test.dart
@@ -23,6 +23,9 @@
@override
String get lintCode => LintNames.avoid_returning_null_for_future;
+ @override
+ String? get testPackageLanguageVersion => '2.9';
+
Future<void> test_asyncFor() async {
await resolveTestCode('''
Future<String> f() {
diff --git a/pkg/analysis_server/test/src/services/correction/fix/wrap_in_text_test.dart b/pkg/analysis_server/test/src/services/correction/fix/wrap_in_text_test.dart
index bca0c26..3774eb1 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/wrap_in_text_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/wrap_in_text_test.dart
@@ -50,7 +50,7 @@
await resolveTestCode('''
typedef F = void Function();
-void foo({F a}) {}
+void foo({F? a}) {}
void bar() {
foo(a: '');
diff --git a/pkg/analysis_server/test/src/services/flutter/widget_descriptions_test.dart b/pkg/analysis_server/test/src/services/flutter/widget_descriptions_test.dart
index 4592d32..c2a1b33 100644
--- a/pkg/analysis_server/test/src/services/flutter/widget_descriptions_test.dart
+++ b/pkg/analysis_server/test/src/services/flutter/widget_descriptions_test.dart
@@ -442,7 +442,7 @@
}
class MyWidget<T> {
- MyWidget({int xxx = 0, @required Widget child});
+ MyWidget({int xxx = 0, required Widget child});
}
''');
var property = await getWidgetProperty('MyWidget<int>', 'xxx');
@@ -463,7 +463,7 @@
}
class MyWidget<T> {
- MyWidget({int xxx = 0, @required Widget child});
+ MyWidget({int xxx = 0, required Widget child});
}
''');
}
@@ -479,7 +479,7 @@
}
class MyWidget<T> {
- MyWidget({int xxx = 0, @required List<Widget> children});
+ MyWidget({int xxx = 0, required List<Widget> children});
}
''');
var property = await getWidgetProperty('MyWidget<int>', 'xxx');
@@ -500,7 +500,7 @@
}
class MyWidget<T> {
- MyWidget({int xxx = 0, @required List<Widget> children});
+ MyWidget({int xxx = 0, required List<Widget> children});
}
''');
}
diff --git a/pkg/analysis_server/test/src/utilities/flutter_test.dart b/pkg/analysis_server/test/src/utilities/flutter_test.dart
index c7e6fbc..efdff5a 100644
--- a/pkg/analysis_server/test/src/utilities/flutter_test.dart
+++ b/pkg/analysis_server/test/src/utilities/flutter_test.dart
@@ -17,6 +17,11 @@
@reflectiveTest
class FlutterTest extends AbstractSingleUnitTest {
+ @override
+ // TODO(brianwilkerson) Update these tests. I believe that will require
+ // updating the mock flutter package.
+ String? get testPackageLanguageVersion => '2.9';
+
Flutter get _flutter => Flutter.instance;
@override
diff --git a/pkg/dartdev/lib/dartdev.dart b/pkg/dartdev/lib/dartdev.dart
index ed23c1e..5895deb 100644
--- a/pkg/dartdev/lib/dartdev.dart
+++ b/pkg/dartdev/lib/dartdev.dart
@@ -10,8 +10,8 @@
import 'package:args/command_runner.dart';
import 'package:cli_util/cli_logging.dart';
import 'package:dart_style/src/cli/format_command.dart';
+import 'package:dartdev/src/commands/migrate.dart';
import 'package:meta/meta.dart';
-import 'package:nnbd_migration/migration_cli.dart';
import 'package:pedantic/pedantic.dart';
import 'package:pub/pub.dart';
import 'package:usage/usage.dart';
diff --git a/pkg/dartdev/lib/src/commands/migrate.dart b/pkg/dartdev/lib/src/commands/migrate.dart
new file mode 100644
index 0000000..cb83e82
--- /dev/null
+++ b/pkg/dartdev/lib/src/commands/migrate.dart
@@ -0,0 +1,43 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:analyzer/src/dart/analysis/experiments.dart';
+import 'package:dartdev/src/core.dart';
+import 'package:nnbd_migration/migration_cli.dart';
+
+class MigrateCommand extends DartdevCommand {
+ static const String cmdName = 'migrate';
+
+ static const String cmdDescription =
+ 'Perform null safety migration on a project.';
+
+ /// Return whether the SDK has null safety on by default.
+ static bool get nullSafetyOnByDefault => IsEnabledByDefault.non_nullable;
+
+ final bool verbose;
+
+ MigrateCommand({this.verbose = false})
+ : super(cmdName, '$cmdDescription\n\n${MigrationCli.migrationGuideLink}',
+ verbose) {
+ MigrationCli.defineOptions(argParser, !verbose);
+ }
+
+ @override
+ String get invocation {
+ return '${super.invocation} [project or directory]';
+ }
+
+ @override
+ FutureOr<int> run() async {
+ var cli = MigrationCli(binaryName: 'dart $name');
+ try {
+ await cli.decodeCommandLineArgs(argResults, isVerbose: verbose)?.run();
+ } on MigrationExit catch (migrationExit) {
+ return migrationExit.exitCode;
+ }
+ return 0;
+ }
+}
diff --git a/pkg/nnbd_migration/bin/migrate.dart b/pkg/nnbd_migration/bin/migrate.dart
index ee52742..aac7966 100644
--- a/pkg/nnbd_migration/bin/migrate.dart
+++ b/pkg/nnbd_migration/bin/migrate.dart
@@ -9,7 +9,7 @@
void main(List<String> args) async {
var cli = MigrationCli(binaryName: 'nnbd_migration');
- ArgResults argResults;
+ late ArgResults argResults;
try {
try {
argResults = MigrationCli.createParser().parse(args);
diff --git a/pkg/nnbd_migration/lib/instrumentation.dart b/pkg/nnbd_migration/lib/instrumentation.dart
index 98be08f..fb1afee 100644
--- a/pkg/nnbd_migration/lib/instrumentation.dart
+++ b/pkg/nnbd_migration/lib/instrumentation.dart
@@ -21,22 +21,22 @@
final int offset;
/// Name of the enclosing function, or `null` if not known.
- String function;
+ String? function;
CodeReference(this.path, this.offset, this.line, this.column, this.function);
/// Creates a [CodeReference] pointing to the given [node].
factory CodeReference.fromAstNode(AstNode node) {
- var compilationUnit = node.thisOrAncestorOfType<CompilationUnit>();
- var source = compilationUnit.declaredElement.source;
- var location = compilationUnit.lineInfo.getLocation(node.offset);
+ var compilationUnit = node.thisOrAncestorOfType<CompilationUnit>()!;
+ var source = compilationUnit.declaredElement!.source;
+ var location = compilationUnit.lineInfo!.getLocation(node.offset);
return CodeReference(source.fullName, node.offset, location.lineNumber,
location.columnNumber, _computeEnclosingName(node));
}
factory CodeReference.fromElement(
Element element, LineInfo Function(String) getLineInfo) {
- var path = element.source.fullName;
+ var path = element.source!.fullName;
var offset = element.nameOffset;
var location = getLineInfo(path).getLocation(offset);
return CodeReference(path, offset, location.lineNumber,
@@ -59,7 +59,7 @@
return '${function ?? 'unknown'} ($pathAsUri:$line:$column)';
}
- static String _computeElementFullName(Element element) {
+ static String? _computeElementFullName(Element? element) {
List<String> parts = [];
while (element != null) {
var elementName = _computeElementName(element);
@@ -72,21 +72,21 @@
return parts.reversed.join('.');
}
- static String _computeElementName(Element element) {
+ static String? _computeElementName(Element element) {
if (element is CompilationUnitElement || element is LibraryElement) {
return null;
}
return element.name;
}
- static String _computeEnclosingName(AstNode node) {
- List<String> parts = [];
+ static String? _computeEnclosingName(AstNode? node) {
+ List<String?> parts = [];
while (node != null) {
var nodeName = _computeNodeDeclarationName(node);
if (nodeName != null) {
parts.add(nodeName);
} else if (parts.isEmpty && node is VariableDeclarationList) {
- parts.add(node.variables.first.declaredElement.name);
+ parts.add(node.variables.first.declaredElement!.name);
}
node = node.parent;
}
@@ -94,7 +94,7 @@
return parts.reversed.join('.');
}
- static String _computeNodeDeclarationName(AstNode node) {
+ static String? _computeNodeDeclarationName(AstNode node) {
if (node is ExtensionDeclaration) {
return node.declaredElement?.name ?? '<unnamed extension>';
} else if (node is Declaration) {
@@ -111,33 +111,33 @@
abstract class DecoratedTypeInfo {
/// Information about the graph node associated with the decision of whether
/// or not to make this type into a nullable type.
- NullabilityNodeInfo get node;
+ NullabilityNodeInfo? get node;
/// If [type] is a function type, information about the set of nullability
/// nodes decorating the type's return type.
- DecoratedTypeInfo get returnType;
+ DecoratedTypeInfo? get returnType;
/// The original (pre-migration) type that is being migrated.
- DartType get type;
+ DartType? get type;
/// If [type] is a function type, looks up information about the set of
/// nullability nodes decorating one of the type's named parameter types.
- DecoratedTypeInfo namedParameter(String name);
+ DecoratedTypeInfo? namedParameter(String name);
/// If [type] is a function type, looks up information about the set of
/// nullability nodes decorating one of the type's positional parameter types.
/// (This could be an optional or a required positional parameter).
- DecoratedTypeInfo positionalParameter(int i);
+ DecoratedTypeInfo? positionalParameter(int i);
/// If [type] is an interface type, looks up information about the set of
/// nullability nodes decorating one of the type's type arguments.
- DecoratedTypeInfo typeArgument(int i);
+ DecoratedTypeInfo? typeArgument(int i);
}
/// Information about a propagation step that occurred during downstream
/// propagation.
abstract class DownstreamPropagationStepInfo implements PropagationStepInfo {
- DownstreamPropagationStepInfo get principalCause;
+ DownstreamPropagationStepInfo? get principalCause;
/// The node whose nullability was changed.
///
@@ -145,7 +145,7 @@
/// Propagation steps that are pending but have not taken effect yet, or that
/// never had an effect (e.g. because an edge was not triggered) will have a
/// `null` value for this field.
- NullabilityNodeInfo get targetNode;
+ NullabilityNodeInfo? get targetNode;
}
/// Information exposed to the migration client about an edge in the nullability
@@ -167,7 +167,7 @@
/// important to satisfy the graph edge. (Typically this is because the code
/// that led to the graph edge being created is only reachable if the guards
/// are all nullable).
- Iterable<NullabilityNodeInfo> get guards;
+ Iterable<NullabilityNodeInfo?> get guards;
/// A boolean indicating whether the graph edge is a "hard" edge. Hard edges
/// are associated with unconditional control flow, and thus allow information
@@ -207,7 +207,7 @@
bool get isUpstreamTriggered;
/// Information about the graph node that this edge "points away from".
- NullabilityNodeInfo get sourceNode;
+ NullabilityNodeInfo? get sourceNode;
}
/// Information exposed to the migration client about the location in source
@@ -218,21 +218,21 @@
/// corresponding element; otherwise `null`.
///
/// Note that either [node] or [element] will always be non-null.
- Element get element;
+ Element? get element;
/// The kind of origin represented by this info.
- EdgeOriginKind get kind;
+ EdgeOriginKind? get kind;
/// If the proximate cause of the edge being introduced into the graph
/// corresponds to an AST node in a source file that is being migrated, the
/// corresponding AST node; otherwise `null`.
///
/// Note that either [node] or [element] will always be non-null.
- AstNode get node;
+ AstNode? get node;
/// If [node] is non-null, the source file that it appears in. Otherwise
/// `null`.
- Source get source;
+ Source? get source;
}
/// An enumeration of the various kinds of edge origins created by the migration
@@ -292,7 +292,7 @@
/// lookups afterwards.
abstract class NodeMapper extends NodeToIdMapper {
/// Gets the node corresponding to the given [id].
- NullabilityNodeInfo nodeForId(int id);
+ NullabilityNodeInfo? nodeForId(int? id);
}
/// Abstract interface for assigning ids numbers to nodes.
@@ -309,14 +309,14 @@
///
/// The format of the changes is a map from source file offset to a list of
/// changes to be applied at that offset.
- void changes(Source source, Map<int, List<AtomicEdit>> changes);
+ void changes(Source source, Map<int?, List<AtomicEdit>> changes);
/// Called whenever an explicit [typeAnnotation] is found in the source code,
/// to report the nullability [node] that was associated with this type. If
/// the migration engine determines that the [node] should be nullable, a `?`
/// will be inserted after the type annotation.
void explicitTypeNullability(
- Source source, TypeAnnotation typeAnnotation, NullabilityNodeInfo node);
+ Source? source, TypeAnnotation typeAnnotation, NullabilityNodeInfo? node);
/// Called whenever reference is made to an [element] outside of the code
/// being migrated, to report the nullability nodes associated with the type
@@ -351,7 +351,7 @@
/// function type alias declaration, GenericFunctionType, or a function
/// expression.
void implicitReturnType(
- Source source, AstNode node, DecoratedTypeInfo decoratedReturnType);
+ Source? source, AstNode node, DecoratedTypeInfo? decoratedReturnType);
/// Called whenever the migration engine encounters an implicit type
/// associated with an AST node, to report the nullability nodes associated
@@ -361,7 +361,7 @@
/// parameter, a declared identifier, or a variable in a variable declaration
/// list.
void implicitType(
- Source source, AstNode node, DecoratedTypeInfo decoratedType);
+ Source? source, AstNode? node, DecoratedTypeInfo decoratedType);
/// Called whenever the migration engine encounters an AST node with implicit
/// type arguments, to report the nullability nodes associated with the
@@ -372,7 +372,7 @@
/// invocation, instance creation expression, list/map/set literal, or type
/// annotation.
void implicitTypeArguments(
- Source source, AstNode node, Iterable<DecoratedTypeInfo> types);
+ Source? source, AstNode node, Iterable<DecoratedTypeInfo> types);
/// Clear any data from the propagation step in preparation for that step
/// being re-run.
@@ -387,7 +387,7 @@
/// Source code location corresponding to this nullability node, or `null` if
/// not known.
- CodeReference get codeReference;
+ CodeReference? get codeReference;
/// Some nodes get nullability from downstream, so the downstream edges are
/// available to query as well.
@@ -399,7 +399,7 @@
/// Each edit is represented as a [Map<int, List<AtomicEdit>>] as is typical
/// of [AtomicEdit]s since they do not have an offset. See extensions
/// [AtomicEditMap] and [AtomicEditList] for usage.
- Map<HintActionKind, Map<int, List<AtomicEdit>>> get hintActions;
+ Map<HintActionKind, Map<int?, List<AtomicEdit>>> get hintActions;
/// After migration is complete, this getter can be used to query whether
/// the type associated with this node was determined to be "exact nullable."
@@ -419,19 +419,19 @@
/// If [isNullable] is false, the propagation step that caused this node to
/// become non-nullable (if any).
- UpstreamPropagationStepInfo get whyNotNullable;
+ UpstreamPropagationStepInfo? get whyNotNullable;
/// If [isNullable] is true, the propagation step that caused this node to
/// become nullable.
- DownstreamPropagationStepInfo get whyNullable;
+ DownstreamPropagationStepInfo? get whyNullable;
}
abstract class PropagationStepInfo {
- CodeReference get codeReference;
+ CodeReference? get codeReference;
/// The nullability edge associated with this propagation step, if any.
/// Otherwise `null`.
- EdgeInfo get edge;
+ EdgeInfo? get edge;
}
/// Reason information for a simple fix that isn't associated with any edges or
@@ -461,7 +461,7 @@
}
@override
- NullabilityNodeInfo nodeForId(int id) => _idToNode[id];
+ NullabilityNodeInfo? nodeForId(int? id) => _idToNode[id!];
}
/// Information exposed to the migration client about a node in the nullability
@@ -495,5 +495,5 @@
/// `null` value for this field.
NullabilityNodeInfo get node;
- UpstreamPropagationStepInfo get principalCause;
+ UpstreamPropagationStepInfo? get principalCause;
}
diff --git a/pkg/nnbd_migration/lib/migration_cli.dart b/pkg/nnbd_migration/lib/migration_cli.dart
index f1b09ac..a9893c0 100644
--- a/pkg/nnbd_migration/lib/migration_cli.dart
+++ b/pkg/nnbd_migration/lib/migration_cli.dart
@@ -14,7 +14,6 @@
import 'package:analyzer/file_system/physical_file_system.dart';
import 'package:analyzer/src/dart/analysis/analysis_context_collection.dart';
import 'package:analyzer/src/dart/analysis/driver_based_analysis_context.dart';
-import 'package:analyzer/src/dart/analysis/experiments.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/util/sdk.dart';
@@ -22,7 +21,6 @@
hide AnalysisError;
import 'package:args/args.dart';
import 'package:cli_util/cli_logging.dart';
-import 'package:dartdev/src/core.dart';
import 'package:meta/meta.dart';
import 'package:nnbd_migration/src/edit_plan.dart';
import 'package:nnbd_migration/src/exceptions.dart';
@@ -35,7 +33,7 @@
import 'package:nnbd_migration/src/utilities/source_edit_diff_formatter.dart';
import 'package:path/path.dart' show Context;
-String _pluralize(int count, String single, {String multiple}) {
+String _pluralize(int count, String single, {String? multiple}) {
return count == 1 ? single : (multiple ?? '${single}s');
}
@@ -47,7 +45,7 @@
/// [AnalysisError]s.
class AnalysisResult {
final List<AnalysisError> errors;
- final Map<String, LineInfo> lineInfo;
+ final Map<String?, LineInfo> lineInfo;
final Context pathContext;
final String rootDirectory;
final bool allSourcesAlreadyMigrated;
@@ -74,7 +72,7 @@
var result = <Map<String, dynamic>>[];
// severity • Message ... at foo/bar.dart:6:1 • (error_code)
for (var error in errors) {
- var lineInfoForThisFile = lineInfo[error.source.fullName];
+ var lineInfoForThisFile = lineInfo[error.source.fullName]!;
var location = lineInfoForThisFile.getLocation(error.offset);
var path =
pathContext.relative(error.source.fullName, from: rootDirectory);
@@ -108,69 +106,33 @@
final String directory;
- final bool ignoreErrors;
+ final bool? ignoreErrors;
- final bool ignoreExceptions;
+ final bool? ignoreExceptions;
- final String previewHostname;
+ final String? previewHostname;
- final int previewPort;
+ final int? previewPort;
final String sdkPath;
- final bool skipImportCheck;
+ final bool? skipImportCheck;
- final String summary;
+ final String? summary;
- final bool webPreview;
+ final bool? webPreview;
CommandLineOptions(
- {@required this.applyChanges,
- @required this.directory,
- @required this.ignoreErrors,
- @required this.ignoreExceptions,
- @required this.previewHostname,
- @required this.previewPort,
- @required this.sdkPath,
- @required this.skipImportCheck,
- @required this.summary,
- @required this.webPreview});
-}
-
-class MigrateCommand extends DartdevCommand {
- static const String cmdName = 'migrate';
-
- static const String cmdDescription =
- 'Perform null safety migration on a project.';
-
- static const String migrationGuideLink =
- 'See https://dart.dev/go/null-safety-migration for a migration guide.';
-
- /// Return whether the SDK has null safety on by default.
- static bool get nullSafetyOnByDefault => IsEnabledByDefault.non_nullable;
-
- final bool verbose;
-
- MigrateCommand({this.verbose = false})
- : super(cmdName, '$cmdDescription\n\n$migrationGuideLink', verbose) {
- MigrationCli._defineOptions(argParser, !verbose);
- }
-
- @override
- String get invocation {
- return '${super.invocation} [project or directory]';
- }
-
- @override
- FutureOr<int> run() async {
- var cli = MigrationCli(binaryName: 'dart $name');
- try {
- await cli.decodeCommandLineArgs(argResults, isVerbose: verbose)?.run();
- } on MigrationExit catch (migrationExit) {
- return migrationExit.exitCode;
- }
- return 0;
- }
+ {required this.applyChanges,
+ required this.directory,
+ required this.ignoreErrors,
+ required this.ignoreExceptions,
+ required this.previewHostname,
+ required this.previewPort,
+ required this.sdkPath,
+ required this.skipImportCheck,
+ required this.summary,
+ required this.webPreview});
}
/// Command-line API for the migration tool, with additional parameters exposed
@@ -281,12 +243,15 @@
)),
];
+ static const String migrationGuideLink =
+ 'See https://dart.dev/go/null-safety-migration for a migration guide.';
+
/// The name of the executable, for reporting in help messages.
final String binaryName;
/// The SDK path that should be used if none is provided by the user. Used in
/// testing to install a mock SDK.
- final String defaultSdkPathOverride;
+ final String? defaultSdkPathOverride;
/// Factory to create an appropriate Logger instance to give feedback to the
/// user. Used in testing to allow user feedback messages to be tested.
@@ -304,11 +269,11 @@
final Map<String, String> _environmentVariables;
MigrationCli({
- @required this.binaryName,
+ required this.binaryName,
@visibleForTesting this.loggerFactory = _defaultLoggerFactory,
@visibleForTesting this.defaultSdkPathOverride,
- @visibleForTesting ResourceProvider resourceProvider,
- @visibleForTesting Map<String, String> environmentVariables,
+ @visibleForTesting ResourceProvider? resourceProvider,
+ @visibleForTesting Map<String, String>? environmentVariables,
}) : logger = loggerFactory(false),
resourceProvider =
resourceProvider ?? PhysicalResourceProvider.INSTANCE,
@@ -324,12 +289,12 @@
///
/// If the user supplied a bad option, a message is printed using the logger
/// configured in the constructor, and [MigrationExit] is thrown.
- MigrationCliRunner decodeCommandLineArgs(ArgResults argResults,
- {bool isVerbose}) {
+ MigrationCliRunner? decodeCommandLineArgs(ArgResults argResults,
+ {bool? isVerbose}) {
try {
- isVerbose ??= argResults[CommandLineOptions.verboseFlag] as bool;
+ isVerbose ??= argResults[CommandLineOptions.verboseFlag] as bool?;
if (argResults[CommandLineOptions.helpFlag] as bool) {
- _showUsage(isVerbose);
+ _showUsage(isVerbose!);
return null;
}
var rest = argResults.rest;
@@ -353,43 +318,44 @@
var applyChanges =
argResults[CommandLineOptions.applyChangesFlag] as bool;
var previewPortRaw =
- argResults[CommandLineOptions.previewPortOption] as String;
- int previewPort;
+ argResults[CommandLineOptions.previewPortOption] as String?;
+ int? previewPort;
try {
previewPort = previewPortRaw == null ? null : int.parse(previewPortRaw);
} on FormatException catch (_) {
throw _BadArgException(
'Invalid value for --${CommandLineOptions.previewPortOption}');
}
- bool webPreview;
+ bool? webPreview;
if (argResults.wasParsed(CommandLineOptions.webPreviewFlag)) {
- webPreview = argResults[CommandLineOptions.webPreviewFlag] as bool;
+ webPreview = argResults[CommandLineOptions.webPreviewFlag] as bool?;
} else {
// If the `webPreviewFlag` wasn't explicitly passed, then the value of
// this option is based on the value of the [applyChanges] option.
webPreview = !applyChanges;
}
- if (applyChanges && webPreview) {
+ if (applyChanges && webPreview!) {
throw _BadArgException('--apply-changes requires --no-web-preview');
}
var options = CommandLineOptions(
applyChanges: applyChanges,
directory: migratePath,
- ignoreErrors: argResults[CommandLineOptions.ignoreErrorsFlag] as bool,
+ ignoreErrors:
+ argResults[CommandLineOptions.ignoreErrorsFlag] as bool?,
ignoreExceptions:
- argResults[CommandLineOptions.ignoreExceptionsFlag] as bool,
+ argResults[CommandLineOptions.ignoreExceptionsFlag] as bool?,
previewHostname:
- argResults[CommandLineOptions.previewHostnameOption] as String,
+ argResults[CommandLineOptions.previewHostnameOption] as String?,
previewPort: previewPort,
- sdkPath: argResults[CommandLineOptions.sdkPathOption] as String ??
+ sdkPath: argResults[CommandLineOptions.sdkPathOption] as String? ??
defaultSdkPathOverride ??
getSdkPath(),
skipImportCheck:
- argResults[CommandLineOptions.skipImportCheckFlag] as bool,
- summary: argResults[CommandLineOptions.summaryOption] as String,
+ argResults[CommandLineOptions.skipImportCheckFlag] as bool?,
+ summary: argResults[CommandLineOptions.summaryOption] as String?,
webPreview: webPreview);
return MigrationCliRunner(this, options,
- logger: isVerbose ? loggerFactory(true) : null);
+ logger: isVerbose! ? loggerFactory(true) : null);
} on Object catch (exception) {
handleArgParsingException(exception);
}
@@ -432,10 +398,16 @@
'Display this help message. Add --verbose to show hidden options.',
defaultsTo: false,
negatable: false);
- _defineOptions(parser, hide);
+ defineOptions(parser, hide);
return parser;
}
+ static void defineOptions(ArgParser parser, bool hide) {
+ for (var option in options) {
+ option.addToParser(parser, hide);
+ }
+ }
+
static Logger _defaultLoggerFactory(bool isVerbose) {
var ansi = Ansi(Ansi.terminalSupportsAnsi);
if (isVerbose) {
@@ -444,12 +416,6 @@
return Logger.standard(ansi: ansi);
}
}
-
- static void _defineOptions(ArgParser parser, bool hide) {
- for (var option in options) {
- option.addToParser(parser, hide);
- }
- }
}
/// Data structure representing a single command-line option to the migration
@@ -488,25 +454,25 @@
/// The result of parsing command-line options.
final CommandLineOptions options;
- final Map<String, LineInfo> lineInfo = {};
+ final Map<String?, LineInfo> lineInfo = {};
- DartFixListener _dartFixListener;
+ DartFixListener? _dartFixListener;
- _FixCodeProcessor _fixCodeProcessor;
+ _FixCodeProcessor? _fixCodeProcessor;
- AnalysisContextCollectionImpl _contextCollection;
+ AnalysisContextCollectionImpl? _contextCollection;
bool _hasExceptions = false;
bool _hasAnalysisErrors = false;
/// Subscription of interrupt signals (control-C).
- StreamSubscription<ProcessSignal> _sigIntSubscription;
+ StreamSubscription<ProcessSignal>? _sigIntSubscription;
/// Completes when an interrupt signal (control-C) is received.
- Completer<void> sigIntSignalled;
+ late Completer<void> sigIntSignalled;
- MigrationCliRunner(this.cli, this.options, {Logger logger})
+ MigrationCliRunner(this.cli, this.options, {Logger? logger})
: logger = logger ?? cli.logger;
@visibleForTesting
@@ -514,15 +480,15 @@
// Handle the case of more than one analysis context being found (typically,
// the current directory and one or more sub-directories).
if (hasMultipleAnalysisContext) {
- return contextCollection.contextFor(options.directory);
+ return contextCollection!.contextFor(options.directory);
} else {
- return contextCollection.contexts.single;
+ return contextCollection!.contexts.single;
}
}
Ansi get ansi => logger.ansi;
- AnalysisContextCollectionImpl get contextCollection {
+ AnalysisContextCollectionImpl? get contextCollection {
_contextCollection ??= AnalysisContextCollectionImpl(
includedPaths: [options.directory],
resourceProvider: resourceProvider,
@@ -532,7 +498,7 @@
@visibleForTesting
bool get hasMultipleAnalysisContext {
- return contextCollection.contexts.length > 1;
+ return contextCollection!.contexts.length > 1;
}
@visibleForTesting
@@ -549,7 +515,7 @@
/// Computes the internet address that should be passed to `HttpServer.bind`
/// when starting the preview server. May be overridden in derived classes.
- Object computeBindAddress() {
+ Object? computeBindAddress() {
var hostname = options.previewHostname;
if (hostname == 'localhost') {
return InternetAddress.loopbackIPv4;
@@ -583,13 +549,13 @@
DartFixListener listener,
ResourceProvider resourceProvider,
LineInfo Function(String path) getLineInfo,
- Object bindAddress,
+ Object? bindAddress,
{List<String> included = const <String>[],
- int preferredPort,
- String summaryPath,
- @required String sdkPath}) {
+ int? preferredPort,
+ String? summaryPath,
+ required String sdkPath}) {
return NonNullableFix(listener, resourceProvider, getLineInfo, bindAddress,
- logger, (String path) => shouldBeMigrated(path),
+ logger, (String? path) => shouldBeMigrated(path!),
included: included,
preferredPort: preferredPort,
summaryPath: summaryPath,
@@ -611,7 +577,7 @@
@override
void onException(String detail) {
if (_hasExceptions) {
- if (!options.ignoreExceptions) {
+ if (!options.ignoreExceptions!) {
// Our intention is to exit immediately when an exception occurred. We
// tried, but failed (probably due to permissive mode logic in the
// migration tool itself catching the MigrationExit exception). The
@@ -623,7 +589,7 @@
return;
}
_hasExceptions = true;
- if (options.ignoreExceptions) {
+ if (options.ignoreExceptions!) {
logger.stdout('''
Exception(s) occurred during migration. Attempting to perform
migration anyway due to the use of --${CommandLineOptions.ignoreExceptionsFlag}.
@@ -679,7 +645,7 @@
logger.stdout('Migrating ${options.directory}');
logger.stdout('');
- logger.stdout(MigrateCommand.migrationGuideLink);
+ logger.stdout(MigrationCli.migrationGuideLink);
logger.stdout('');
if (hasMultipleAnalysisContext) {
@@ -694,21 +660,21 @@
_fixCodeProcessor = _FixCodeProcessor(analysisContext, this);
_dartFixListener = DartFixListener(
DriverProviderImpl(resourceProvider, analysisContext), this);
- nonNullableFix = createNonNullableFix(_dartFixListener, resourceProvider,
- _fixCodeProcessor.getLineInfo, computeBindAddress(),
+ nonNullableFix = createNonNullableFix(_dartFixListener!, resourceProvider,
+ _fixCodeProcessor!.getLineInfo, computeBindAddress(),
included: [options.directory],
preferredPort: options.previewPort,
summaryPath: options.summary,
sdkPath: options.sdkPath);
nonNullableFix.rerunFunction = _rerunFunction;
- _fixCodeProcessor.registerCodeTask(nonNullableFix);
+ _fixCodeProcessor!.registerCodeTask(nonNullableFix);
try {
- var analysisResult = await _fixCodeProcessor.runFirstPhase();
+ var analysisResult = await _fixCodeProcessor!.runFirstPhase();
if (analysisResult.hasErrors) {
_logErrors(analysisResult);
- if (!options.ignoreErrors) {
+ if (!options.ignoreErrors!) {
throw MigrationExit(1);
}
} else if (analysisResult.allSourcesAlreadyMigrated) {
@@ -728,12 +694,12 @@
logger.stdout('');
logger.stdout(ansi.emphasized('Generating migration suggestions...'));
- var previewUrls = (await _fixCodeProcessor.runLaterPhases()).previewUrls;
+ var previewUrls = (await _fixCodeProcessor!.runLaterPhases()).previewUrls;
if (options.applyChanges) {
logger.stdout(ansi.emphasized('Applying changes:'));
- var allEdits = _dartFixListener.sourceChange.edits;
+ var allEdits = _dartFixListener!.sourceChange.edits;
_applyMigrationSuggestions(allEdits);
logger.stdout('');
@@ -746,11 +712,11 @@
return;
}
- if (options.webPreview) {
- assert(previewUrls.length == 1,
+ if (options.webPreview!) {
+ assert(previewUrls!.length == 1,
'Got unexpected extra preview URLs from server');
- var url = previewUrls.single;
+ var url = previewUrls!.single;
// TODO(#41809): Open a browser automatically.
logger.stdout('''
View the migration suggestions by visiting:
@@ -778,7 +744,7 @@
} else {
logger.stdout(ansi.emphasized('Diff of changes:'));
- _displayChangeDiff(_dartFixListener);
+ _displayChangeDiff(_dartFixListener!);
logger.stdout('');
logger.stdout('To apply these changes, re-run the tool with '
@@ -817,7 +783,7 @@
int count = sourceFileEdit.edits.length;
logger.stdout(' $relPath ($count ${_pluralize(count, 'change')})');
- String source;
+ String? source;
var file = resourceProvider.getFile(sourceFileEdit.file);
try {
source = file.readAsStringSync();
@@ -843,7 +809,7 @@
for (DartFixSuggestion suggestion in migrationResults.suggestions) {
String file = suggestion.location.file;
fileSuggestions.putIfAbsent(file, () => <DartFixSuggestion>[]);
- fileSuggestions[file].add(suggestion);
+ fileSuggestions[file]!.add(suggestion);
}
// present a diff-like view
@@ -858,7 +824,7 @@
logger.stdout('${ansi.emphasized(relPath)} '
'($count ${_pluralize(count, 'change')}):');
- String source;
+ String? source;
try {
source = resourceProvider.getFile(file).readAsStringSync();
} catch (_) {}
@@ -893,7 +859,7 @@
logger.stdout('');
_hasAnalysisErrors = true;
- if (options.ignoreErrors) {
+ if (options.ignoreErrors!) {
logger.stdout('Note: analysis errors will result in erroneous migration '
'suggestions.');
logger.stdout('Continuing with migration suggestions due to the use of '
@@ -931,32 +897,32 @@
Future<MigrationState> _rerunFunction() async {
logger.stdout(ansi.emphasized('Re-analyzing project...'));
- _dartFixListener.reset();
- _fixCodeProcessor.prepareToRerun();
- var analysisResult = await _fixCodeProcessor.runFirstPhase();
- if (analysisResult.hasErrors && !options.ignoreErrors) {
+ _dartFixListener!.reset();
+ _fixCodeProcessor!.prepareToRerun();
+ var analysisResult = await _fixCodeProcessor!.runFirstPhase();
+ if (analysisResult.hasErrors && !options.ignoreErrors!) {
_logErrors(analysisResult);
return MigrationState(
- _fixCodeProcessor._task.migration,
- _fixCodeProcessor._task.includedRoot,
+ _fixCodeProcessor!._task!.migration,
+ _fixCodeProcessor!._task!.includedRoot,
_dartFixListener,
- _fixCodeProcessor._task.instrumentationListener,
+ _fixCodeProcessor!._task!.instrumentationListener,
{},
- _fixCodeProcessor._task.shouldBeMigratedFunction,
+ _fixCodeProcessor!._task!.shouldBeMigratedFunction,
analysisResult);
} else if (analysisResult.allSourcesAlreadyMigrated) {
_logAlreadyMigrated();
return MigrationState(
- _fixCodeProcessor._task.migration,
- _fixCodeProcessor._task.includedRoot,
+ _fixCodeProcessor!._task!.migration,
+ _fixCodeProcessor!._task!.includedRoot,
_dartFixListener,
- _fixCodeProcessor._task.instrumentationListener,
+ _fixCodeProcessor!._task!.instrumentationListener,
{},
- _fixCodeProcessor._task.shouldBeMigratedFunction,
+ _fixCodeProcessor!._task!.shouldBeMigratedFunction,
analysisResult);
} else {
logger.stdout(ansi.emphasized('Re-generating migration suggestions...'));
- return await _fixCodeProcessor.runLaterPhases();
+ return await _fixCodeProcessor!.runLaterPhases();
}
}
@@ -994,7 +960,7 @@
/// Run a process synchronously, as in [Process.runSync].
ProcessResult runSync(String executable, List<String> arguments,
- {String workingDirectory});
+ {String? workingDirectory});
}
/// A [ProcessManager] that directs all method calls to static methods of
@@ -1003,7 +969,7 @@
const SystemProcessManager();
ProcessResult runSync(String executable, List<String> arguments,
- {String workingDirectory}) =>
+ {String? workingDirectory}) =>
Process.runSync(executable, arguments,
workingDirectory: workingDirectory ?? Directory.current.path);
}
@@ -1020,11 +986,11 @@
final DriverBasedAnalysisContext context;
/// The task used to migrate to NNBD.
- NonNullableFix _task;
+ NonNullableFix? _task;
Set<String> pathsToProcess;
- ProgressBar _progressBar;
+ late ProgressBar _progressBar;
final MigrationCliRunner _migrationCli;
@@ -1046,14 +1012,14 @@
Future<void> processResources(
Future<void> Function(ResolvedUnitResult result) process) async {
var driver = context.currentSession;
- var pathsProcessed = <String>{};
+ var pathsProcessed = <String?>{};
for (var path in pathsToProcess) {
if (pathsProcessed.contains(path)) continue;
var result = await driver.getResolvedLibrary2(path);
// Parts will either be found in a library, below, or if the library
// isn't [isIncluded], will be picked up in the final loop.
if (result is ResolvedLibraryResult) {
- for (var unit in result.units) {
+ for (var unit in result.units!) {
if (!pathsProcessed.contains(unit.path)) {
await process(unit);
pathsProcessed.add(unit.path);
@@ -1083,7 +1049,7 @@
// Process each source file.
bool allSourcesAlreadyMigrated = true;
await processResources((ResolvedUnitResult result) async {
- if (!result.unit.featureSet.isEnabled(Feature.non_nullable)) {
+ if (!result.unit!.featureSet.isEnabled(Feature.non_nullable)) {
allSourcesAlreadyMigrated = false;
}
_progressBar.tick();
@@ -1094,14 +1060,14 @@
analysisErrors.addAll(errors);
_migrationCli.lineInfo[result.path] = result.lineInfo;
}
- if (_migrationCli.options.ignoreErrors || analysisErrors.isEmpty) {
- await _task.prepareUnit(result);
+ if (_migrationCli.options.ignoreErrors! || analysisErrors.isEmpty) {
+ await _task!.prepareUnit(result);
}
});
- var unmigratedDependencies = _task.migration.unmigratedDependencies;
+ var unmigratedDependencies = _task!.migration!.unmigratedDependencies;
if (unmigratedDependencies.isNotEmpty) {
- if (_migrationCli.options.skipImportCheck) {
+ if (_migrationCli.options.skipImportCheck!) {
_migrationCli.logger.stdout(unmigratedDependenciesWarning);
} else {
throw ExperimentStatusException.unmigratedDependencies(
@@ -1123,25 +1089,25 @@
await processResources((ResolvedUnitResult result) async {
_progressBar.tick();
- await _task.processUnit(result);
+ await _task!.processUnit(result);
});
await processResources((ResolvedUnitResult result) async {
_progressBar.tick();
- if (_migrationCli.shouldBeMigrated(result.path)) {
- await _task.finalizeUnit(result);
+ if (_migrationCli.shouldBeMigrated(result.path!)) {
+ await _task!.finalizeUnit(result);
}
});
_progressBar.complete();
_migrationCli.logger.stdout(_migrationCli.ansi
.emphasized('Compiling instrumentation information...'));
// Update the tasks paths-to-process, in case of new or deleted files.
- _task.pathsToProcess = pathsToProcess;
- var state = await _task.finish();
- _task.processPackage(context.contextRoot.root, state.neededPackages);
- if (_migrationCli.options.webPreview) {
- await _task.startPreviewServer(state, _migrationCli.applyHook);
+ _task!.pathsToProcess = pathsToProcess;
+ var state = await _task!.finish();
+ _task!.processPackage(context.contextRoot.root, state.neededPackages);
+ if (_migrationCli.options.webPreview!) {
+ await _task!.startPreviewServer(state, _migrationCli.applyHook);
}
- state.previewUrls = _task.previewUrls;
+ state.previewUrls = _task!.previewUrls;
return state;
}
@@ -1152,14 +1118,14 @@
final Logger logger;
final String rootDirectory;
final Context pathContext;
- final Map<String, LineInfo> lineInfo;
+ final Map<String?, LineInfo> lineInfo;
_IssueRenderer(
this.logger, this.rootDirectory, this.pathContext, this.lineInfo);
void render(AnalysisError issue) {
// severity • Message ... at foo/bar.dart:6:1 • (error_code)
- var lineInfoForThisFile = lineInfo[issue.source.fullName];
+ var lineInfoForThisFile = lineInfo[issue.source.fullName]!;
var location = lineInfoForThisFile.getLocation(issue.offset);
final Ansi ansi = logger.ansi;
@@ -1185,6 +1151,5 @@
case Severity.info:
return 'info';
}
- return '???';
}
}
diff --git a/pkg/nnbd_migration/lib/nnbd_migration.dart b/pkg/nnbd_migration/lib/nnbd_migration.dart
index 6dd3991..e79df1f 100644
--- a/pkg/nnbd_migration/lib/nnbd_migration.dart
+++ b/pkg/nnbd_migration/lib/nnbd_migration.dart
@@ -9,7 +9,6 @@
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/utilities_general.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart';
-import 'package:meta/meta.dart';
import 'package:nnbd_migration/instrumentation.dart';
import 'package:nnbd_migration/src/nullability_migration_impl.dart';
import 'package:pub_semver/pub_semver.dart';
@@ -184,10 +183,10 @@
/// A formal parameter needs to have a required keyword added.
factory NullabilityFixDescription.addRequired(
- String className, String functionName, String paramName) =>
+ String? className, String? functionName, String paramName) =>
NullabilityFixDescription._(
appliedMessage: "Add 'required' keyword to parameter '$paramName' in " +
- (className == null ? functionName : "'$className.$functionName'"),
+ (className == null ? functionName! : "'$className.$functionName'"),
kind: NullabilityFixKind.addRequired,
);
@@ -246,9 +245,7 @@
);
const NullabilityFixDescription._(
- {@required this.appliedMessage, @required this.kind})
- : assert(appliedMessage != null),
- assert(kind != null);
+ {required this.appliedMessage, required this.kind});
@override
int get hashCode {
@@ -321,15 +318,15 @@
/// Optional parameter [warnOnWeakCode] indicates whether weak-only code
/// should be warned about or removed (in the way specified by
/// [removeViaComments]).
- factory NullabilityMigration(NullabilityMigrationListener listener,
+ factory NullabilityMigration(NullabilityMigrationListener? listener,
LineInfo Function(String) getLineInfo,
- {bool permissive,
- NullabilityMigrationInstrumentation instrumentation,
- bool removeViaComments,
- bool warnOnWeakCode}) = NullabilityMigrationImpl;
+ {bool? permissive,
+ NullabilityMigrationInstrumentation? instrumentation,
+ bool? removeViaComments,
+ bool? warnOnWeakCode}) = NullabilityMigrationImpl;
/// Check if this migration is being run permissively.
- bool get isPermissive;
+ bool? get isPermissive;
/// Use this getter after any calls to [prepareInput] to obtain a list of URIs
/// of unmigrated dependencies. Ideally, this list should be empty before the
@@ -367,5 +364,5 @@
/// "permissive mode", reporting the location of the exception and the
/// exception details.
void reportException(
- Source source, AstNode node, Object exception, StackTrace stackTrace);
+ Source? source, AstNode? node, Object exception, StackTrace stackTrace);
}
diff --git a/pkg/nnbd_migration/lib/src/already_migrated_code_decorator.dart b/pkg/nnbd_migration/lib/src/already_migrated_code_decorator.dart
index 7bc800c..ae86a52 100644
--- a/pkg/nnbd_migration/lib/src/already_migrated_code_decorator.dart
+++ b/pkg/nnbd_migration/lib/src/already_migrated_code_decorator.dart
@@ -52,7 +52,7 @@
}
if (type is FunctionType) {
for (var element in type.typeFormals) {
- DecoratedTypeParameterBounds.current.put(
+ DecoratedTypeParameterBounds.current!.put(
element,
decorate(
element.bound ??
diff --git a/pkg/nnbd_migration/lib/src/conditional_discard.dart b/pkg/nnbd_migration/lib/src/conditional_discard.dart
index abecbd5..c94b5ea 100644
--- a/pkg/nnbd_migration/lib/src/conditional_discard.dart
+++ b/pkg/nnbd_migration/lib/src/conditional_discard.dart
@@ -21,7 +21,7 @@
///
/// `null` if the code path should be kept regardless of the outcome of
/// migration.
- final NullabilityNode trueGuard;
+ final NullabilityNode? trueGuard;
/// Nullability node that will be `nullable` if the code path that results
/// from the condition evaluating to `false` will be reachable after
@@ -29,7 +29,7 @@
///
/// `null` if the code path should be kept regardless of the outcome of
/// migration.
- final NullabilityNode falseGuard;
+ final NullabilityNode? falseGuard;
/// Indicates whether the condition is pure (free from side effects).
///
@@ -47,11 +47,11 @@
/// Indicates whether the code path that results from the condition evaluating
/// to `false` is reachable after migration.
- bool get keepFalse => falseGuard == null || falseGuard.isNullable;
+ bool get keepFalse => falseGuard == null || falseGuard!.isNullable;
/// Indicates whether the code path that results from the condition evaluating
/// to `true` is reachable after migration.
- bool get keepTrue => trueGuard == null || trueGuard.isNullable;
+ bool get keepTrue => trueGuard == null || trueGuard!.isNullable;
- FixReasonInfo get reason => !keepTrue ? trueGuard : falseGuard;
+ FixReasonInfo? get reason => !keepTrue ? trueGuard : falseGuard;
}
diff --git a/pkg/nnbd_migration/lib/src/decorated_class_hierarchy.dart b/pkg/nnbd_migration/lib/src/decorated_class_hierarchy.dart
index d35fd15..71c8a70 100644
--- a/pkg/nnbd_migration/lib/src/decorated_class_hierarchy.dart
+++ b/pkg/nnbd_migration/lib/src/decorated_class_hierarchy.dart
@@ -15,7 +15,7 @@
/// For instance, if one class is a subclass of the other, we record the
/// nullabilities of all the types involved in the subclass relationship.
class DecoratedClassHierarchy {
- final Variables _variables;
+ final Variables? _variables;
final NullabilityGraph _graph;
@@ -30,12 +30,12 @@
///
/// If the [type] is a [TypeParameterType], it will be resolved against its
/// bound.
- DecoratedType asInstanceOf(DecoratedType type, ClassElement superclass) {
+ DecoratedType asInstanceOf(DecoratedType type, ClassElement? superclass) {
type = _getInterfaceType(type);
var typeType = type.type as InterfaceType;
var class_ = typeType.element;
if (class_ == superclass) return type;
- var result = getDecoratedSupertype(class_, superclass);
+ var result = getDecoratedSupertype(class_, superclass!);
if (result.typeArguments.isNotEmpty && type.typeArguments.isNotEmpty) {
// TODO(paulberry): test
result = result.substitute(type.asSubstitution);
@@ -77,14 +77,14 @@
// superclasses relates to all of its transitive superclasses.
decorations = {};
var decoratedDirectSupertypes =
- _variables.decoratedDirectSupertypes(class_);
+ _variables!.decoratedDirectSupertypes(class_);
for (var entry in decoratedDirectSupertypes.entries) {
var superclass = entry.key;
- var decoratedSupertype = entry.value;
+ var decoratedSupertype = entry.value!;
var supertype = decoratedSupertype.type as InterfaceType;
// Compute a type substitution to determine how [class_] relates to
// this specific [superclass].
- Map<TypeParameterElement, DecoratedType> substitution = {};
+ Map<TypeParameterElement, DecoratedType?> substitution = {};
for (int i = 0; i < supertype.typeArguments.length; i++) {
substitution[supertype.element.typeParameters[i]] =
decoratedSupertype.typeArguments[i];
@@ -114,7 +114,7 @@
if (typeType is TypeParameterType) {
final innerType = _getInterfaceType(
- _variables.decoratedTypeParameterBound(typeType.element));
+ _variables!.decoratedTypeParameterBound(typeType.element)!);
return type.substitute({typeType.element: innerType});
}
diff --git a/pkg/nnbd_migration/lib/src/decorated_type.dart b/pkg/nnbd_migration/lib/src/decorated_type.dart
index a6c6e30..b32a622 100644
--- a/pkg/nnbd_migration/lib/src/decorated_type.dart
+++ b/pkg/nnbd_migration/lib/src/decorated_type.dart
@@ -18,28 +18,28 @@
/// indicating whether the type, and the types that compose it, are nullable.
class DecoratedType implements DecoratedTypeInfo {
@override
- final DartType type;
+ final DartType? type;
@override
- final NullabilityNode node;
+ final NullabilityNode? node;
@override
- final DecoratedType returnType;
+ final DecoratedType? returnType;
/// If `this` is a function type, the [DecoratedType] of each of its
/// positional parameters (including both required and optional positional
/// parameters).
- final List<DecoratedType> positionalParameters;
+ final List<DecoratedType?>? positionalParameters;
/// If `this` is a function type, the [DecoratedType] of each of its named
/// parameters.
- final Map<String, DecoratedType> namedParameters;
+ final Map<String, DecoratedType?>? namedParameters;
/// If `this` is a parameterized type, the [DecoratedType] of each of its
/// type parameters.
///
/// TODO(paulberry): how should we handle generic typedefs?
- final List<DecoratedType> typeArguments;
+ final List<DecoratedType?> typeArguments;
DecoratedType(this.type, this.node,
{this.returnType,
@@ -51,39 +51,39 @@
var type = this.type;
if (type is InterfaceType) {
assert(returnType == null);
- assert(positionalParameters.isEmpty);
- assert(namedParameters.isEmpty);
+ assert(positionalParameters!.isEmpty);
+ assert(namedParameters!.isEmpty);
assert(typeArguments.length == type.typeArguments.length);
for (int i = 0; i < typeArguments.length; i++) {
- assert(typeArguments[i].type == type.typeArguments[i],
- '${typeArguments[i].type} != ${type.typeArguments[i]}');
+ assert(typeArguments[i]!.type == type.typeArguments[i],
+ '${typeArguments[i]!.type} != ${type.typeArguments[i]}');
}
} else if (type is FunctionType) {
- assert(returnType.type == type.returnType);
+ assert(returnType!.type == type.returnType);
int positionalParameterCount = 0;
int namedParameterCount = 0;
for (var parameter in type.parameters) {
if (parameter.isNamed) {
- assert(namedParameters[parameter.name].type == parameter.type);
+ assert(namedParameters![parameter.name]!.type == parameter.type);
namedParameterCount++;
} else {
- assert(positionalParameters[positionalParameterCount].type ==
+ assert(positionalParameters![positionalParameterCount]!.type ==
parameter.type);
positionalParameterCount++;
}
}
- assert(positionalParameters.length == positionalParameterCount);
- assert(namedParameters.length == namedParameterCount);
+ assert(positionalParameters!.length == positionalParameterCount);
+ assert(namedParameters!.length == namedParameterCount);
assert(typeArguments.isEmpty);
} else if (node is TypeParameterType) {
assert(returnType == null);
- assert(positionalParameters.isEmpty);
- assert(namedParameters.isEmpty);
+ assert(positionalParameters!.isEmpty);
+ assert(namedParameters!.isEmpty);
assert(typeArguments.isEmpty);
} else {
assert(returnType == null);
- assert(positionalParameters.isEmpty);
- assert(namedParameters.isEmpty);
+ assert(positionalParameters!.isEmpty);
+ assert(namedParameters!.isEmpty);
assert(typeArguments.isEmpty);
}
return true;
@@ -99,7 +99,7 @@
NullabilityNode node,
NullabilityGraph graph,
NullabilityNodeTarget target,
- {DecoratedType returnType}) {
+ {DecoratedType? returnType}) {
var positionalParameters = <DecoratedType>[];
var namedParameters = <String, DecoratedType>{};
int index = 0;
@@ -114,8 +114,8 @@
}
}
for (var element in type.typeFormals) {
- if (DecoratedTypeParameterBounds.current.get(element) == null) {
- DecoratedTypeParameterBounds.current.put(
+ if (DecoratedTypeParameterBounds.current!.get(element) == null) {
+ DecoratedTypeParameterBounds.current!.put(
element,
DecoratedType.forImplicitType(
typeProvider,
@@ -136,15 +136,15 @@
/// nodes everywhere that don't correspond to any source location. These
/// nodes can later be unioned with other nodes.
factory DecoratedType.forImplicitType(TypeProvider typeProvider,
- DartType type, NullabilityGraph graph, NullabilityNodeTarget target,
- {List<DecoratedType> typeArguments}) {
+ DartType? type, NullabilityGraph graph, NullabilityNodeTarget target,
+ {List<DecoratedType?>? typeArguments}) {
var nullabilityNode = NullabilityNode.forInferredType(target);
if (type is InterfaceType) {
assert(() {
if (typeArguments != null) {
assert(typeArguments.length == type.typeArguments.length);
for (var i = 0; i < typeArguments.length; ++i) {
- assert(typeArguments[i].type == type.typeArguments[i]);
+ assert(typeArguments[i]!.type == type.typeArguments[i]);
}
}
return true;
@@ -195,10 +195,10 @@
/// For instance, if `this` represents `List<int?1>`, returns the substitution
/// `{T: int?1}`, where `T` is the [TypeParameterElement] for `List`'s type
/// parameter.
- Map<TypeParameterElement, DecoratedType> get asSubstitution {
+ Map<TypeParameterElement, DecoratedType?> get asSubstitution {
var type = this.type;
if (type is InterfaceType) {
- return Map<TypeParameterElement, DecoratedType>.fromIterables(
+ return Map<TypeParameterElement, DecoratedType?>.fromIterables(
type.element.typeParameters, typeArguments);
} else {
throw StateError(
@@ -208,7 +208,7 @@
/// If this type is a function type, returns its generic formal parameters.
/// Otherwise returns `null`.
- List<TypeParameterElement> get typeFormals {
+ List<TypeParameterElement>? get typeFormals {
var type = this.type;
if (type is FunctionType) {
return type.typeFormals;
@@ -266,7 +266,7 @@
Map<TypeParameterElement, DecoratedType> substitution = {};
for (int i = 0; i < argumentTypes.length; i++) {
var argumentType = argumentTypes[i];
- undecoratedArgumentTypes.add(argumentType.type);
+ undecoratedArgumentTypes.add(argumentType.type!);
substitution[typeFormals[i]] = argumentType;
}
return _substituteFunctionAfterFormals(
@@ -274,10 +274,10 @@
}
@override
- DecoratedTypeInfo namedParameter(String name) => namedParameters[name];
+ DecoratedTypeInfo? namedParameter(String name) => namedParameters![name];
@override
- DecoratedTypeInfo positionalParameter(int i) => positionalParameters[i];
+ DecoratedTypeInfo? positionalParameter(int i) => positionalParameters![i];
/// Updates the [roles] map with information about the nullability nodes
/// pointed to by this decorated type.
@@ -287,18 +287,19 @@
/// represent a type argument (or a positional parameter type, in the case of
/// a function type), an name to represent a named parameter type, or `@r` to
/// represent a return type.
- void recordRoles(Map<String, NullabilityNode> roles,
+ void recordRoles(Map<String, NullabilityNode?> roles,
{String rolePrefix = ''}) {
roles[rolePrefix] = node;
returnType?.recordRoles(roles, rolePrefix: '$rolePrefix/@r');
- for (int i = 0; i < positionalParameters.length; i++) {
- positionalParameters[i].recordRoles(roles, rolePrefix: '$rolePrefix/$i');
+ for (int i = 0; i < positionalParameters!.length; i++) {
+ positionalParameters![i]!
+ .recordRoles(roles, rolePrefix: '$rolePrefix/$i');
}
- for (var entry in namedParameters.entries) {
- entry.value.recordRoles(roles, rolePrefix: '$rolePrefix/${entry.key}');
+ for (var entry in namedParameters!.entries) {
+ entry.value!.recordRoles(roles, rolePrefix: '$rolePrefix/${entry.key}');
}
for (int i = 0; i < typeArguments.length; i++) {
- typeArguments[i].recordRoles(roles, rolePrefix: '$rolePrefix/$i');
+ typeArguments[i]!.recordRoles(roles, rolePrefix: '$rolePrefix/$i');
}
}
@@ -307,20 +308,20 @@
/// [undecoratedResult] is the result of the substitution, as determined by
/// the normal type system. If not supplied, it is inferred.
DecoratedType substitute(
- Map<TypeParameterElement, DecoratedType> substitution,
- [DartType undecoratedResult]) {
+ Map<TypeParameterElement, DecoratedType?> substitution,
+ [DartType? undecoratedResult]) {
if (substitution.isEmpty) return this;
if (undecoratedResult == null) {
- var type = this.type;
+ var type = this.type!;
undecoratedResult = Substitution.fromPairs(
substitution.keys.toList(),
- substitution.values.map((d) => d.type).toList(),
+ substitution.values.map((d) => d!.type!).toList(),
).substituteType(type);
if (undecoratedResult is FunctionType && type is FunctionType) {
for (int i = 0; i < undecoratedResult.typeFormals.length; i++) {
- DecoratedTypeParameterBounds.current.put(
+ DecoratedTypeParameterBounds.current!.put(
undecoratedResult.typeFormals[i],
- DecoratedTypeParameterBounds.current.get(type.typeFormals[i]));
+ DecoratedTypeParameterBounds.current!.get(type.typeFormals[i]));
}
}
}
@@ -329,7 +330,7 @@
@override
String toString() {
- var trailing = node == null ? '' : node.debugSuffix;
+ var trailing = node == null ? '' : node!.debugSuffix;
var type = this.type;
if (type is TypeParameterType || type is VoidType) {
return '$type$trailing';
@@ -346,19 +347,19 @@
formals = '<${type.typeFormals.join(', ')}>';
}
List<String> paramStrings = [];
- for (int i = 0; i < positionalParameters.length; i++) {
+ for (int i = 0; i < positionalParameters!.length; i++) {
var prefix = '';
if (i == type.normalParameterTypes.length) {
prefix = '[';
}
- paramStrings.add('$prefix${positionalParameters[i]}');
+ paramStrings.add('$prefix${positionalParameters![i]}');
}
- if (type.normalParameterTypes.length < positionalParameters.length) {
+ if (type.normalParameterTypes.length < positionalParameters!.length) {
paramStrings.last += ']';
}
- if (namedParameters.isNotEmpty) {
+ if (namedParameters!.isNotEmpty) {
var prefix = '{';
- for (var entry in namedParameters.entries) {
+ for (var entry in namedParameters!.entries) {
paramStrings.add('$prefix${entry.key}: ${entry.value}');
prefix = '';
}
@@ -368,7 +369,7 @@
return '$returnType Function$formals($args)$trailing';
} else if (type is DynamicTypeImpl) {
return 'dynamic';
- } else if (type.isBottom) {
+ } else if (type!.isBottom) {
return 'Never$trailing';
} else {
throw '$type'; // TODO(paulberry)
@@ -376,10 +377,10 @@
}
@override
- DecoratedTypeInfo typeArgument(int i) => typeArguments[i];
+ DecoratedTypeInfo? typeArgument(int i) => typeArguments[i];
/// Creates a shallow copy of `this`, replacing the nullability node.
- DecoratedType withNode(NullabilityNode node) => DecoratedType(type, node,
+ DecoratedType withNode(NullabilityNode? node) => DecoratedType(type, node,
returnType: returnType,
positionalParameters: positionalParameters,
namedParameters: namedParameters,
@@ -387,7 +388,7 @@
/// Creates a shallow copy of `this`, replacing the nullability node and the
/// type.
- DecoratedType withNodeAndType(NullabilityNode node, DartType type) =>
+ DecoratedType withNodeAndType(NullabilityNode node, DartType? type) =>
DecoratedType(type, node,
returnType: returnType,
positionalParameters: positionalParameters,
@@ -396,8 +397,8 @@
/// Internal implementation of [_substitute], used as a recursion target.
DecoratedType _substitute(
- Map<TypeParameterElement, DecoratedType> substitution,
- DartType undecoratedResult) {
+ Map<TypeParameterElement, DecoratedType?> substitution,
+ DartType? undecoratedResult) {
var type = this.type;
if (type is FunctionType && undecoratedResult is FunctionType) {
var typeFormals = type.typeFormals;
@@ -419,7 +420,7 @@
for (int i = 0; i < typeFormals.length; i++) {
var typeFormal = typeFormals[i];
var oldDecoratedBound =
- DecoratedTypeParameterBounds.current.get(typeFormal);
+ DecoratedTypeParameterBounds.current!.get(typeFormal);
var undecoratedResult2 = undecoratedResult.typeFormals[i].bound;
if (undecoratedResult2 == null) {
if (oldDecoratedBound == null) {
@@ -434,11 +435,11 @@
undecoratedResult2 = oldDecoratedBound.type;
}
var newDecoratedBound =
- oldDecoratedBound._substitute(substitution, undecoratedResult2);
+ oldDecoratedBound!._substitute(substitution, undecoratedResult2);
if (identical(typeFormal, undecoratedResult.typeFormals[i])) {
assert(oldDecoratedBound == newDecoratedBound);
} else {
- DecoratedTypeParameterBounds.current
+ DecoratedTypeParameterBounds.current!
.put(typeFormal, newDecoratedBound);
}
}
@@ -447,7 +448,7 @@
} else if (type is InterfaceType && undecoratedResult is InterfaceType) {
List<DecoratedType> newTypeArguments = [];
for (int i = 0; i < typeArguments.length; i++) {
- newTypeArguments.add(typeArguments[i]
+ newTypeArguments.add(typeArguments[i]!
.substitute(substitution, undecoratedResult.typeArguments[i]));
}
return DecoratedType(undecoratedResult, node,
@@ -461,7 +462,7 @@
NullabilityNode.forSubstitution(inner.node, node),
undecoratedResult);
}
- } else if (type.isVoid || type.isDynamic) {
+ } else if (type!.isVoid || type.isDynamic) {
return this;
}
throw '$type.substitute($type | $substitution)'; // TODO(paulberry)
@@ -472,35 +473,35 @@
/// is [undecoratedResult], and whose return type, positional parameters, and
/// named parameters are formed by performing the given [substitution].
DecoratedType _substituteFunctionAfterFormals(FunctionType undecoratedResult,
- Map<TypeParameterElement, DecoratedType> substitution) {
+ Map<TypeParameterElement, DecoratedType?> substitution) {
var newPositionalParameters = <DecoratedType>[];
var numRequiredParameters = undecoratedResult.normalParameterTypes.length;
- for (int i = 0; i < positionalParameters.length; i++) {
+ for (int i = 0; i < positionalParameters!.length; i++) {
var undecoratedParameterType = i < numRequiredParameters
? undecoratedResult.normalParameterTypes[i]
: undecoratedResult.optionalParameterTypes[i - numRequiredParameters];
- newPositionalParameters.add(positionalParameters[i]
+ newPositionalParameters.add(positionalParameters![i]!
._substitute(substitution, undecoratedParameterType));
}
var newNamedParameters = <String, DecoratedType>{};
- for (var entry in namedParameters.entries) {
+ for (var entry in namedParameters!.entries) {
var name = entry.key;
var undecoratedParameterType =
undecoratedResult.namedParameterTypes[name];
newNamedParameters[name] =
- (entry.value._substitute(substitution, undecoratedParameterType));
+ (entry.value!._substitute(substitution, undecoratedParameterType));
}
return DecoratedType(undecoratedResult, node,
returnType:
- returnType._substitute(substitution, undecoratedResult.returnType),
+ returnType!._substitute(substitution, undecoratedResult.returnType),
positionalParameters: newPositionalParameters,
namedParameters: newNamedParameters);
}
static bool _compareLists(
- List<DecoratedType> list1, List<DecoratedType> list2) {
+ List<DecoratedType?>? list1, List<DecoratedType?>? list2) {
if (identical(list1, list2)) return true;
- if (list1.length != list2.length) return false;
+ if (list1!.length != list2!.length) return false;
for (int i = 0; i < list1.length; i++) {
if (list1[i] != list2[i]) return false;
}
@@ -508,9 +509,9 @@
}
static bool _compareMaps(
- Map<String, DecoratedType> map1, Map<String, DecoratedType> map2) {
+ Map<String, DecoratedType?>? map1, Map<String, DecoratedType?>? map2) {
if (identical(map1, map2)) return true;
- if (map1.length != map2.length) return false;
+ if (map1!.length != map2!.length) return false;
for (var entry in map1.entries) {
if (entry.value != map2[entry.key]) return false;
}
@@ -530,13 +531,13 @@
///
/// If `null`, then attempts to look up the decorated types of type parameter
/// bounds will fail.
- static DecoratedTypeParameterBounds current;
+ static DecoratedTypeParameterBounds? current;
final _orphanBounds = Expando<DecoratedType>();
- final _parentedBounds = <TypeParameterElement, DecoratedType>{};
+ final _parentedBounds = <TypeParameterElement, DecoratedType?>{};
- DecoratedType get(TypeParameterElement element) {
+ DecoratedType? get(TypeParameterElement element) {
if (element.enclosingElement == null) {
return _orphanBounds[element];
} else {
@@ -544,7 +545,7 @@
}
}
- void put(TypeParameterElement element, DecoratedType bounds) {
+ void put(TypeParameterElement element, DecoratedType? bounds) {
if (element.enclosingElement == null) {
_orphanBounds[element] = bounds;
} else {
@@ -556,17 +557,17 @@
/// Helper class that renames the type parameters in two decorated function
/// types so that they match.
class RenamedDecoratedFunctionTypes {
- final DecoratedType returnType1;
+ final DecoratedType? returnType1;
- final DecoratedType returnType2;
+ final DecoratedType? returnType2;
- final List<DecoratedType> positionalParameters1;
+ final List<DecoratedType?>? positionalParameters1;
- final List<DecoratedType> positionalParameters2;
+ final List<DecoratedType?>? positionalParameters2;
- final Map<String, DecoratedType> namedParameters1;
+ final Map<String, DecoratedType?>? namedParameters1;
- final Map<String, DecoratedType> namedParameters2;
+ final Map<String, DecoratedType?>? namedParameters2;
RenamedDecoratedFunctionTypes._(
this.returnType1,
@@ -585,7 +586,7 @@
///
/// If such a renaming can be found, it is returned. If not, `null` is
/// returned.
- static RenamedDecoratedFunctionTypes match(
+ static RenamedDecoratedFunctionTypes? match(
DecoratedType type1,
DecoratedType type2,
bool Function(DecoratedType, DecoratedType) boundsMatcher) {
@@ -603,33 +604,35 @@
var substitution1 = <TypeParameterElement, DecoratedType>{};
var substitution2 = <TypeParameterElement, DecoratedType>{};
var newParameters = <TypeParameterElement>[];
- for (int i = 0; i < type1.typeFormals.length; i++) {
+ for (int i = 0; i < type1.typeFormals!.length; i++) {
var newParameter =
- TypeParameterElementImpl.synthetic(type1.typeFormals[i].name);
+ TypeParameterElementImpl.synthetic(type1.typeFormals![i].name);
newParameters.add(newParameter);
var newParameterType =
DecoratedType._forTypeParameterSubstitution(newParameter);
- substitution1[type1.typeFormals[i]] = newParameterType;
- substitution2[type2.typeFormals[i]] = newParameterType;
+ substitution1[type1.typeFormals![i]] = newParameterType;
+ substitution2[type2.typeFormals![i]] = newParameterType;
}
- for (int i = 0; i < type1.typeFormals.length; i++) {
- var bound1 = DecoratedTypeParameterBounds.current
- .get((type1.type as FunctionType).typeFormals[i])
+ for (int i = 0; i < type1.typeFormals!.length; i++) {
+ var bound1 = DecoratedTypeParameterBounds.current!
+ .get((type1.type as FunctionType).typeFormals[i])!
.substitute(substitution1);
- var bound2 = DecoratedTypeParameterBounds.current
- .get((type2.type as FunctionType).typeFormals[i])
+ var bound2 = DecoratedTypeParameterBounds.current!
+ .get((type2.type as FunctionType).typeFormals[i])!
.substitute(substitution2);
if (!boundsMatcher(bound1, bound2)) return null;
- DecoratedTypeParameterBounds.current.put(newParameters[i], bound1);
+ DecoratedTypeParameterBounds.current!.put(newParameters[i], bound1);
}
- var returnType1 = type1.returnType.substitute(substitution1);
- var returnType2 = type2.returnType.substitute(substitution2);
+ var returnType1 = type1.returnType!.substitute(substitution1);
+ var returnType2 = type2.returnType!.substitute(substitution2);
var positionalParameters1 =
- _substituteList(type1.positionalParameters, substitution1);
+ _substituteList(type1.positionalParameters!, substitution1);
var positionalParameters2 =
- _substituteList(type2.positionalParameters, substitution2);
- var namedParameters1 = _substituteMap(type1.namedParameters, substitution1);
- var namedParameters2 = _substituteMap(type2.namedParameters, substitution2);
+ _substituteList(type2.positionalParameters!, substitution2);
+ var namedParameters1 =
+ _substituteMap(type1.namedParameters!, substitution1);
+ var namedParameters2 =
+ _substituteMap(type2.namedParameters!, substitution2);
return RenamedDecoratedFunctionTypes._(
returnType1,
returnType2,
@@ -639,27 +642,27 @@
namedParameters2);
}
- static bool _isNeeded(List<TypeParameterElement> formals1,
- List<TypeParameterElement> formals2) {
+ static bool _isNeeded(List<TypeParameterElement>? formals1,
+ List<TypeParameterElement>? formals2) {
if (identical(formals1, formals2)) return false;
- if (formals1.length != formals2.length) return true;
+ if (formals1!.length != formals2!.length) return true;
for (int i = 0; i < formals1.length; i++) {
if (!identical(formals1[i], formals2[i])) return true;
}
return false;
}
- static List<DecoratedType> _substituteList(List<DecoratedType> list,
+ static List<DecoratedType> _substituteList(List<DecoratedType?> list,
Map<TypeParameterElement, DecoratedType> substitution) {
- return list.map((t) => t.substitute(substitution)).toList();
+ return list.map((t) => t!.substitute(substitution)).toList();
}
static Map<String, DecoratedType> _substituteMap(
- Map<String, DecoratedType> map,
+ Map<String, DecoratedType?> map,
Map<TypeParameterElement, DecoratedType> substitution) {
var result = <String, DecoratedType>{};
for (var entry in map.entries) {
- result[entry.key] = entry.value.substitute(substitution);
+ result[entry.key] = entry.value!.substitute(substitution);
}
return result;
}
diff --git a/pkg/nnbd_migration/lib/src/decorated_type_operations.dart b/pkg/nnbd_migration/lib/src/decorated_type_operations.dart
index 4c776c8..3f38ca4 100644
--- a/pkg/nnbd_migration/lib/src/decorated_type_operations.dart
+++ b/pkg/nnbd_migration/lib/src/decorated_type_operations.dart
@@ -14,7 +14,7 @@
class DecoratedTypeOperations
implements TypeOperations<PromotableElement, DecoratedType> {
final TypeSystem _typeSystem;
- final Variables _variableRepository;
+ final Variables? _variableRepository;
final NullabilityGraph _graph;
DecoratedTypeOperations(
@@ -22,7 +22,7 @@
@override
TypeClassification classifyType(DecoratedType type) {
- if (type.type.isDartCoreNull) {
+ if (type.type!.isDartCoreNull) {
return TypeClassification.nullOrEquivalent;
} else {
return TypeClassification.potentiallyNullable;
@@ -36,10 +36,11 @@
}
@override
- bool forcePromotion(DecoratedType to, DecoratedType from,
- List<DecoratedType> promotedTypes, List<DecoratedType> newPromotedTypes) {
- assert(to != null);
- assert(from != null);
+ bool forcePromotion(
+ DecoratedType to,
+ DecoratedType from,
+ List<DecoratedType>? promotedTypes,
+ List<DecoratedType>? newPromotedTypes) {
// Do not force promotion if it appears that the element's type was just
// demoted.
if (promotedTypes != null &&
@@ -50,7 +51,7 @@
if (!isSubtypeOf(to, from)) {
return false;
}
- var fromSources = from.node.upstreamEdges;
+ var fromSources = from.node!.upstreamEdges;
// Do not force promotion if [to] already points to [from].
if (fromSources.length == 1 && fromSources.single.sourceNode == to.node) {
return false;
@@ -70,7 +71,7 @@
@override
bool isSubtypeOf(DecoratedType leftType, DecoratedType rightType) {
- if (!_typeSystem.isSubtypeOf(leftType.type, rightType.type)) {
+ if (!_typeSystem.isSubtypeOf(leftType.type!, rightType.type!)) {
// Pre-migrated types don't meet the subtype requirement. Not a subtype.
return false;
} else if (rightType.node == _graph.never &&
@@ -92,8 +93,8 @@
@override
// This function walks [chain1] and [chain2], creating an intersection similar
// to that of [VariableModel.joinPromotedTypes].
- List<DecoratedType> refinePromotedTypes(List<DecoratedType> chain1,
- List<DecoratedType> chain2, List<DecoratedType> promotedTypes) {
+ List<DecoratedType>? refinePromotedTypes(List<DecoratedType>? chain1,
+ List<DecoratedType>? chain2, List<DecoratedType>? promotedTypes) {
if (chain1 == null || chain2 == null) return promotedTypes;
// This method can only handle very simple joins.
@@ -130,14 +131,14 @@
// TODO(srawlins): How to get the source or astNode from within here...
GreatestLowerBoundOrigin(null /* source */, null /* astNode */);
_graph.connect(firstType.node, node, origin, guards: [secondType.node]);
- _graph.connect(node, firstType.node, origin);
- _graph.connect(node, secondType.node, origin);
+ _graph.connect(node, firstType.node!, origin);
+ _graph.connect(node, secondType.node!, origin);
return result..add(firstType.withNode(node));
}
@override
- DecoratedType tryPromoteToType(DecoratedType to, DecoratedType from) {
+ DecoratedType? tryPromoteToType(DecoratedType to, DecoratedType from) {
// TODO(paulberry): implement appropriate logic for type variable promotion.
if (isSubtypeOf(to, from)) {
return to;
@@ -154,6 +155,6 @@
@override
DecoratedType variableType(PromotableElement variable) {
- return _variableRepository.decoratedElementType(variable);
+ return _variableRepository!.decoratedElementType(variable);
}
}
diff --git a/pkg/nnbd_migration/lib/src/edge_builder.dart b/pkg/nnbd_migration/lib/src/edge_builder.dart
index c98e62c..42249d9 100644
--- a/pkg/nnbd_migration/lib/src/edge_builder.dart
+++ b/pkg/nnbd_migration/lib/src/edge_builder.dart
@@ -68,18 +68,18 @@
this._decoratedClassHierarchy);
void checkAssignment(EdgeOrigin origin,
- {@required DecoratedType source,
- @required DecoratedType destination,
- @required bool hard}) {
+ {required DecoratedType source,
+ required DecoratedType destination,
+ required bool hard}) {
super._checkAssignment(origin, FixReasonTarget.root,
source: source, destination: destination, hard: hard);
}
@override
- void _connect(NullabilityNode source, NullabilityNode destination,
+ void _connect(NullabilityNode? source, NullabilityNode? destination,
EdgeOrigin origin, FixReasonTarget edgeTarget,
{bool hard = false, bool checkable = true}) {
- _graph.connect(source, destination, origin,
+ _graph.connect(source, destination!, origin,
hard: hard, checkable: checkable);
}
@@ -109,74 +109,74 @@
/// The repository of constraint variables and decorated types (from a
/// previous pass over the source code).
- final Variables _variables;
+ final Variables? _variables;
- final NullabilityMigrationListener /*?*/ listener;
+ final NullabilityMigrationListener? listener;
- final NullabilityMigrationInstrumentation /*?*/ instrumentation;
+ final NullabilityMigrationInstrumentation? instrumentation;
final NullabilityGraph _graph;
TypeProvider typeProvider;
@override
- final Source source;
+ final Source? source;
@override
- final DecoratedClassHierarchy _decoratedClassHierarchy;
+ final DecoratedClassHierarchy? _decoratedClassHierarchy;
/// If we are visiting a function body or initializer, instance of flow
/// analysis. Otherwise `null`.
- FlowAnalysis<AstNode, Statement, Expression, PromotableElement, DecoratedType>
- _flowAnalysis;
+ FlowAnalysis<AstNode, Statement, Expression, PromotableElement,
+ DecoratedType>? _flowAnalysis;
/// If we are visiting a function body or initializer, assigned variable
/// information used in flow analysis. Otherwise `null`.
- AssignedVariables<AstNode, PromotableElement> _assignedVariables;
+ AssignedVariables<AstNode, PromotableElement>? _assignedVariables;
/// The [DecoratedType] of the innermost function or method being visited, or
/// `null` if the visitor is not inside any function or method.
///
/// This is needed to construct the appropriate nullability constraints for
/// return statements.
- DecoratedType _currentFunctionType;
+ DecoratedType? _currentFunctionType;
- FunctionExpression _currentFunctionExpression;
+ FunctionExpression? _currentFunctionExpression;
/// The [ClassElement] or [ExtensionElement] of the current class or extension
/// being visited, or null.
- Element _currentClassOrExtension;
+ Element? _currentClassOrExtension;
/// If an extension declaration is being visited, the decorated type of the
/// type appearing in the `on` clause (this is the type of `this` inside the
/// extension declaration). Null if an extension declaration is not being
/// visited.
- DecoratedType _currentExtendedType;
+ DecoratedType? _currentExtendedType;
/// The [DecoratedType] of the innermost list or set literal being visited, or
/// `null` if the visitor is not inside any list or set.
///
/// This is needed to construct the appropriate nullability constraints for
/// ui as code elements.
- DecoratedType _currentLiteralElementType;
+ DecoratedType? _currentLiteralElementType;
/// The key [DecoratedType] of the innermost map literal being visited, or
/// `null` if the visitor is not inside any map.
///
/// This is needed to construct the appropriate nullability constraints for
/// ui as code elements.
- DecoratedType _currentMapKeyType;
+ DecoratedType? _currentMapKeyType;
/// The value [DecoratedType] of the innermost map literal being visited, or
/// `null` if the visitor is not inside any map.
///
/// This is needed to construct the appropriate nullability constraints for
/// ui as code elements.
- DecoratedType _currentMapValueType;
+ DecoratedType? _currentMapValueType;
/// Information about the most recently visited binary expression whose
/// boolean value could possibly affect nullability analysis.
- _ConditionInfo _conditionInfo;
+ _ConditionInfo? _conditionInfo;
/// The set of nullability nodes that would have to be `nullable` for the code
/// currently being visited to be reachable.
@@ -184,7 +184,7 @@
/// Guard variables are attached to the left hand side of any generated
/// constraints, so that constraints do not take effect if they come from
/// code that can be proven unreachable by the migration tool.
- final _guards = <NullabilityNode>[];
+ final _guards = <NullabilityNode?>[];
/// The scope of locals (parameters, variables) that are post-dominated by the
/// current node as we walk the AST. We use a [_ScopedLocalSet] so that outer
@@ -200,27 +200,27 @@
/// analyze expressions like `a?.b += c`, since the type of the compound
/// assignment is nullable if the type of the expression preceding `?.` is
/// nullable.
- final Map<Expression, NullabilityNode> _conditionalNodes = {};
+ final Map<Expression, NullabilityNode?> _conditionalNodes = {};
/// If we are visiting a cascade expression, the decorated type of the target
/// of the cascade. Otherwise `null`.
- DecoratedType _currentCascadeTargetType;
+ DecoratedType? _currentCascadeTargetType;
/// While visiting a class declaration, set of class fields that lack
/// initializers at their declaration sites.
- Set<FieldElement> _fieldsNotInitializedAtDeclaration;
+ Set<FieldElement?>? _fieldsNotInitializedAtDeclaration;
/// While visiting a constructor, set of class fields that lack initializers
/// at their declaration sites *and* for which we haven't yet found an
/// initializer in the constructor declaration.
- Set<FieldElement> _fieldsNotInitializedByConstructor;
+ Set<FieldElement?>? _fieldsNotInitializedByConstructor;
/// Current nesting depth of [visitTypeName]
int _typeNameNesting = 0;
final Set<PromotableElement> _lateHintedLocals = {};
- final Map<Token, HintComment> _nullCheckHints = {};
+ final Map<Token, HintComment?> _nullCheckHints = {};
/// Helper that assists us in transforming Iterable methods to their "OrNull"
/// equivalents.
@@ -228,12 +228,12 @@
/// Deferred processing that should be performed once we have finished
/// evaluating the decorated type of a method invocation.
- final Map<MethodInvocation, DecoratedType Function(DecoratedType)>
+ final Map<MethodInvocation, DecoratedType Function(DecoratedType?)>
_deferredMethodInvocationProcessing = {};
/// If we are visiting a local function or closure, the set of local variables
/// assigned to so far inside it. Otherwise `null`.
- Set<Element> _elementsWrittenToInLocalFunction;
+ Set<Element>? _elementsWrittenToInLocalFunction;
EdgeBuilder(this.typeProvider, this._typeSystem, this._variables, this._graph,
this.source, this.listener, this._decoratedClassHierarchy,
@@ -251,16 +251,16 @@
/// [targetExpression], if provided, is the expression for the target
/// (receiver) for a method, getter, or setter invocation.
DecoratedType getOrComputeElementType(AstNode node, Element element,
- {DecoratedType targetType, Expression targetExpression}) {
- Map<TypeParameterElement, DecoratedType> substitution;
- Element baseElement = element.declaration;
+ {DecoratedType? targetType, Expression? targetExpression}) {
+ Map<TypeParameterElement, DecoratedType?>? substitution;
+ Element? baseElement = element.declaration;
if (targetType != null) {
- var enclosingElement = baseElement.enclosingElement;
+ var enclosingElement = baseElement!.enclosingElement;
if (enclosingElement is ClassElement) {
- if (targetType.type.resolveToBound(typeProvider.dynamicType)
+ if (targetType.type!.resolveToBound(typeProvider.dynamicType)
is InterfaceType &&
enclosingElement.typeParameters.isNotEmpty) {
- substitution = _decoratedClassHierarchy
+ substitution = _decoratedClassHierarchy!
.asInstanceOf(targetType, enclosingElement)
.asSubstitution;
}
@@ -304,13 +304,13 @@
_checkAssignment(edgeOrigin, FixReasonTarget.root,
source: decoratedTypeArgument,
destination:
- _variables.decoratedTypeParameterBound(typeParameter),
+ _variables!.decoratedTypeParameterBound(typeParameter)!,
hard: true);
}
}
// (2) ensure that the receiver type is assignable to "on" type (with
// those decorated types substituted into it)
- var onType = _variables.decoratedElementType(extensionElement);
+ var onType = _variables!.decoratedElementType(extensionElement);
if (substitution != null) {
onType = onType.substitute(substitution);
}
@@ -333,7 +333,7 @@
baseElement.isSynthetic &&
!baseElement.variable.isSynthetic) {
var variable = baseElement.variable;
- var decoratedElementType = _variables.decoratedElementType(variable);
+ var decoratedElementType = _variables!.decoratedElementType(variable);
if (baseElement.isGetter) {
var target = NullabilityNodeTarget.text('getter function');
decoratedBaseType = DecoratedType(
@@ -349,7 +349,7 @@
NullabilityNode.forInferredType(target.returnType())));
}
} else {
- decoratedBaseType = _variables.decoratedElementType(baseElement);
+ decoratedBaseType = _variables!.decoratedElementType(baseElement!);
}
if (substitution != null) {
return decoratedBaseType.substitute(substitution);
@@ -362,7 +362,7 @@
// TODO(srawlins): Theoretically, edges should be connected between arguments
// and parameters, as in an instance creation. It is quite rare though, to
// declare a class and use it as an annotation in the same package.
- DecoratedType visitAnnotation(Annotation node) {
+ DecoratedType? visitAnnotation(Annotation node) {
var previousFlowAnalysis = _flowAnalysis;
var previousAssignedVariables = _assignedVariables;
if (_flowAnalysis == null) {
@@ -370,7 +370,7 @@
_flowAnalysis = FlowAnalysis<AstNode, Statement, Expression,
PromotableElement, DecoratedType>(
DecoratedTypeOperations(_typeSystem, _variables, _graph),
- _assignedVariables);
+ _assignedVariables!);
}
try {
_dispatch(node.name);
@@ -388,51 +388,51 @@
DecoratedType visitAsExpression(AsExpression node) {
if (BestPracticesVerifier.isUnnecessaryCast(
node, _typeSystem as TypeSystemImpl)) {
- _variables.recordUnnecessaryCast(source, node);
+ _variables!.recordUnnecessaryCast(source, node);
}
_dispatch(node.type);
- final typeNode = _variables.decoratedTypeAnnotation(source, node.type);
+ final typeNode = _variables!.decoratedTypeAnnotation(source, node.type);
_handleAssignment(node.expression, destinationType: typeNode);
- _flowAnalysis.asExpression_end(node.expression, typeNode);
+ _flowAnalysis!.asExpression_end(node.expression, typeNode);
return typeNode;
}
@override
- DecoratedType visitAssertInitializer(AssertInitializer node) {
- _flowAnalysis.assert_begin();
+ DecoratedType? visitAssertInitializer(AssertInitializer node) {
+ _flowAnalysis!.assert_begin();
_checkExpressionNotNull(node.condition);
if (identical(_conditionInfo?.condition, node.condition)) {
- var intentNode = _conditionInfo.trueDemonstratesNonNullIntent;
- if (intentNode != null && _conditionInfo.postDominatingIntent) {
- _graph.makeNonNullable(_conditionInfo.trueDemonstratesNonNullIntent,
+ var intentNode = _conditionInfo!.trueDemonstratesNonNullIntent;
+ if (intentNode != null && _conditionInfo!.postDominatingIntent!) {
+ _graph.makeNonNullable(_conditionInfo!.trueDemonstratesNonNullIntent,
NonNullAssertionOrigin(source, node));
}
}
- _flowAnalysis.assert_afterCondition(node.condition);
+ _flowAnalysis!.assert_afterCondition(node.condition);
_dispatch(node.message);
- _flowAnalysis.assert_end();
+ _flowAnalysis!.assert_end();
return null;
}
@override
- DecoratedType visitAssertStatement(AssertStatement node) {
- _flowAnalysis.assert_begin();
+ DecoratedType? visitAssertStatement(AssertStatement node) {
+ _flowAnalysis!.assert_begin();
_checkExpressionNotNull(node.condition);
if (identical(_conditionInfo?.condition, node.condition)) {
- var intentNode = _conditionInfo.trueDemonstratesNonNullIntent;
- if (intentNode != null && _conditionInfo.postDominatingIntent) {
- _graph.makeNonNullable(_conditionInfo.trueDemonstratesNonNullIntent,
+ var intentNode = _conditionInfo!.trueDemonstratesNonNullIntent;
+ if (intentNode != null && _conditionInfo!.postDominatingIntent!) {
+ _graph.makeNonNullable(_conditionInfo!.trueDemonstratesNonNullIntent,
NonNullAssertionOrigin(source, node));
}
}
- _flowAnalysis.assert_afterCondition(node.condition);
+ _flowAnalysis!.assert_afterCondition(node.condition);
_dispatch(node.message);
- _flowAnalysis.assert_end();
+ _flowAnalysis!.assert_end();
return null;
}
@override
- DecoratedType visitAssignmentExpression(AssignmentExpression node) {
+ DecoratedType? visitAssignmentExpression(AssignmentExpression node) {
bool isQuestionAssign = false;
bool isCompound = false;
if (node.operator.type == TokenType.QUESTION_QUESTION_EQ) {
@@ -445,9 +445,9 @@
if (node.leftHandSide is SimpleIdentifier &&
_isCurrentFunctionExpressionFoundInTestSetUpCall()) {
var assignee =
- getWriteOrReadElement(node.leftHandSide as SimpleIdentifier);
+ getWriteOrReadElement(node.leftHandSide as SimpleIdentifier)!;
var enclosingElementOfCurrentFunction =
- _currentFunctionExpression.declaredElement.enclosingElement;
+ _currentFunctionExpression!.declaredElement!.enclosingElement;
if (enclosingElementOfCurrentFunction == assignee.enclosingElement) {
// [node]'s enclosing function is a function expression passed directly
// to a call to the test package's `setUp` function, and [node] is an
@@ -464,9 +464,9 @@
sourceIsSetupCall: sourceIsSetupCall);
var conditionalNode = _conditionalNodes[node.leftHandSide];
if (conditionalNode != null) {
- expressionType = expressionType.withNode(
+ expressionType = expressionType!.withNode(
NullabilityNode.forLUB(conditionalNode, expressionType.node));
- _variables.recordDecoratedExpressionType(node, expressionType);
+ _variables!.recordDecoratedExpressionType(node, expressionType);
}
return expressionType;
@@ -474,11 +474,11 @@
@override
DecoratedType visitAwaitExpression(AwaitExpression node) {
- var expressionType = _dispatch(node.expression);
+ var expressionType = _dispatch(node.expression)!;
// TODO(paulberry) Handle subclasses of Future.
- if (expressionType.type.isDartAsyncFuture ||
- expressionType.type.isDartAsyncFutureOr) {
- expressionType = expressionType.typeArguments[0];
+ if (expressionType.type!.isDartAsyncFuture ||
+ expressionType.type!.isDartAsyncFutureOr) {
+ expressionType = expressionType.typeArguments[0]!;
}
return expressionType;
}
@@ -489,17 +489,17 @@
var leftOperand = node.leftOperand;
var rightOperand = node.rightOperand;
if (operatorType == TokenType.EQ_EQ || operatorType == TokenType.BANG_EQ) {
- var leftType = _dispatch(leftOperand);
+ var leftType = _dispatch(leftOperand)!;
_graph.connectDummy(leftType.node, DummyOrigin(source, node));
- _flowAnalysis.equalityOp_rightBegin(leftOperand, leftType);
- var rightType = _dispatch(rightOperand);
+ _flowAnalysis!.equalityOp_rightBegin(leftOperand, leftType);
+ var rightType = _dispatch(rightOperand)!;
_graph.connectDummy(rightType.node, DummyOrigin(source, node));
bool notEqual = operatorType == TokenType.BANG_EQ;
- _flowAnalysis.equalityOp_end(node, rightOperand, rightType,
- notEqual: notEqual);
+ _flowAnalysis!
+ .equalityOp_end(node, rightOperand, rightType, notEqual: notEqual);
void buildNullConditionInfo(NullLiteral nullLiteral,
- Expression otherOperand, NullabilityNode otherNode) {
+ Expression otherOperand, NullabilityNode? otherNode) {
assert(nullLiteral != otherOperand);
// TODO(paulberry): only set falseChecksNonNull in unconditional
// control flow
@@ -524,35 +524,35 @@
} else if (operatorType == TokenType.AMPERSAND_AMPERSAND ||
operatorType == TokenType.BAR_BAR) {
bool isAnd = operatorType == TokenType.AMPERSAND_AMPERSAND;
- _flowAnalysis.logicalBinaryOp_begin();
+ _flowAnalysis!.logicalBinaryOp_begin();
_checkExpressionNotNull(leftOperand);
- _flowAnalysis.logicalBinaryOp_rightBegin(node.leftOperand, node,
- isAnd: isAnd);
+ _flowAnalysis!
+ .logicalBinaryOp_rightBegin(node.leftOperand, node, isAnd: isAnd);
_postDominatedLocals.doScoped(
action: () => _checkExpressionNotNull(rightOperand));
- _flowAnalysis.logicalBinaryOp_end(node, rightOperand, isAnd: isAnd);
+ _flowAnalysis!.logicalBinaryOp_end(node, rightOperand, isAnd: isAnd);
return _makeNonNullableBoolType(node);
} else if (operatorType == TokenType.QUESTION_QUESTION) {
DecoratedType expressionType;
- var leftType = _dispatch(leftOperand);
- _flowAnalysis.ifNullExpression_rightBegin(node.leftOperand, leftType);
+ var leftType = _dispatch(leftOperand)!;
+ _flowAnalysis!.ifNullExpression_rightBegin(node.leftOperand, leftType);
try {
_guards.add(leftType.node);
- DecoratedType rightType;
+ DecoratedType? rightType;
_postDominatedLocals.doScoped(action: () {
rightType = _dispatch(rightOperand);
});
var ifNullNode = NullabilityNode.forIfNotNull(node);
expressionType = _decorateUpperOrLowerBound(
- node, node.staticType, leftType, rightType, true,
+ node, node.staticType, leftType, rightType!, true,
node: ifNullNode);
- _connect(rightType.node, expressionType.node,
+ _connect(rightType!.node, expressionType.node,
IfNullOrigin(source, node), null);
} finally {
- _flowAnalysis.ifNullExpression_end();
+ _flowAnalysis!.ifNullExpression_end();
_guards.removeLast();
}
- _variables.recordDecoratedExpressionType(node, expressionType);
+ _variables!.recordDecoratedExpressionType(node, expressionType);
return expressionType;
} else if (operatorType.isUserDefinableOperator) {
var targetType = _checkExpressionNotNull(leftOperand);
@@ -563,10 +563,10 @@
} else {
var calleeType = getOrComputeElementType(node, callee,
targetType: targetType, targetExpression: leftOperand);
- assert(calleeType.positionalParameters.isNotEmpty); // TODO(paulberry)
+ assert(calleeType.positionalParameters!.isNotEmpty); // TODO(paulberry)
_handleAssignment(rightOperand,
- destinationType: calleeType.positionalParameters[0]);
- return _fixNumericTypes(calleeType.returnType, node.staticType);
+ destinationType: calleeType.positionalParameters![0]);
+ return _fixNumericTypes(calleeType.returnType!, node.staticType);
}
} else {
// TODO(paulberry)
@@ -579,14 +579,14 @@
@override
DecoratedType visitBooleanLiteral(BooleanLiteral node) {
- _flowAnalysis.booleanLiteral(node, node.value);
+ _flowAnalysis!.booleanLiteral(node, node.value);
return _makeNonNullLiteralType(node);
}
@override
- DecoratedType visitBreakStatement(BreakStatement node) {
- _flowAnalysis.handleBreak(FlowAnalysisHelper.getLabelTarget(
- node, node.label?.staticElement as LabelElement));
+ DecoratedType? visitBreakStatement(BreakStatement node) {
+ _flowAnalysis!.handleBreak(FlowAnalysisHelper.getLabelTarget(
+ node, node.label?.staticElement as LabelElement?)!);
// Later statements no longer post-dominate the declarations because we
// exited (or, in parent scopes, conditionally exited).
// TODO(mfairhurst): don't clear post-dominators beyond the current loop.
@@ -596,7 +596,7 @@
}
@override
- DecoratedType visitCascadeExpression(CascadeExpression node) {
+ DecoratedType? visitCascadeExpression(CascadeExpression node) {
var oldCascadeTargetType = _currentCascadeTargetType;
try {
_currentCascadeTargetType = _checkExpressionNotNull(node.target);
@@ -608,20 +608,20 @@
}
@override
- DecoratedType visitCatchClause(CatchClause node) {
- _flowAnalysis.tryCatchStatement_catchBegin(
- node.exceptionParameter?.staticElement as PromotableElement,
- node.stackTraceParameter?.staticElement as PromotableElement);
+ DecoratedType? visitCatchClause(CatchClause node) {
+ _flowAnalysis!.tryCatchStatement_catchBegin(
+ node.exceptionParameter?.staticElement as PromotableElement?,
+ node.stackTraceParameter?.staticElement as PromotableElement?);
_dispatch(node.exceptionType);
// The catch clause may not execute, so create a new scope for
// post-dominators.
_postDominatedLocals.doScoped(action: () => _dispatch(node.body));
- _flowAnalysis.tryCatchStatement_catchEnd();
+ _flowAnalysis!.tryCatchStatement_catchEnd();
return null;
}
@override
- DecoratedType visitClassDeclaration(ClassDeclaration node) {
+ DecoratedType? visitClassDeclaration(ClassDeclaration node) {
visitClassOrMixinOrExtensionDeclaration(node);
_dispatch(node.extendsClause);
_dispatch(node.implementsClause);
@@ -630,7 +630,7 @@
return null;
}
- DecoratedType visitClassOrMixinOrExtensionDeclaration(
+ DecoratedType? visitClassOrMixinOrExtensionDeclaration(
CompilationUnitMember node) {
assert(node is ClassOrMixinDeclaration || node is ExtensionDeclaration);
try {
@@ -642,17 +642,17 @@
_fieldsNotInitializedAtDeclaration = {
for (var member in members)
if (member is FieldDeclaration &&
- _variables.getLateHint(source, member.fields) == null)
+ _variables!.getLateHint(source, member.fields) == null)
for (var field in member.fields.variables)
- if (!field.declaredElement.isStatic && field.initializer == null)
- field.declaredElement as FieldElement
+ if (!field.declaredElement!.isStatic && field.initializer == null)
+ (field.declaredElement as FieldElement?)
};
if (_currentClassOrExtension is ClassElement &&
(_currentClassOrExtension as ClassElement)
.unnamedConstructor
?.isSynthetic ==
true) {
- _handleUninitializedFields(node, _fieldsNotInitializedAtDeclaration);
+ _handleUninitializedFields(node, _fieldsNotInitializedAtDeclaration!);
}
_dispatchList(node.metadata);
_dispatchList(members);
@@ -664,24 +664,24 @@
}
@override
- DecoratedType visitClassTypeAlias(ClassTypeAlias node) {
+ DecoratedType? visitClassTypeAlias(ClassTypeAlias node) {
_dispatch(node.superclass);
_dispatch(node.implementsClause);
_dispatch(node.withClause);
- var classElement = node.declaredElement;
- var supertype = classElement.supertype;
+ var classElement = node.declaredElement!;
+ var supertype = classElement.supertype!;
var superElement = supertype.element;
for (var constructorElement in classElement.constructors) {
assert(constructorElement.isSynthetic);
var superConstructorElement =
- superElement.getNamedConstructor(constructorElement.name);
- var constructorDecoratedType = _variables
+ superElement.getNamedConstructor(constructorElement.name)!;
+ var constructorDecoratedType = _variables!
.decoratedElementType(constructorElement)
- .substitute(_decoratedClassHierarchy
+ .substitute(_decoratedClassHierarchy!
.getDecoratedSupertype(classElement, superElement)
.asSubstitution);
var superConstructorDecoratedType =
- _variables.decoratedElementType(superConstructorElement);
+ _variables!.decoratedElementType(superConstructorElement);
var origin = ImplicitMixinSuperCallOrigin(source, node);
_linkDecoratedTypeParameters(
constructorDecoratedType, superConstructorDecoratedType, origin,
@@ -691,78 +691,78 @@
}
@override
- DecoratedType visitComment(Comment node) {
+ DecoratedType? visitComment(Comment node) {
// Ignore comments.
return null;
}
@override
DecoratedType visitConditionalExpression(ConditionalExpression node) {
- _flowAnalysis.conditional_conditionBegin();
+ _flowAnalysis!.conditional_conditionBegin();
_checkExpressionNotNull(node.condition);
- NullabilityNode trueGuard;
- NullabilityNode falseGuard;
+ NullabilityNode? trueGuard;
+ NullabilityNode? falseGuard;
if (identical(_conditionInfo?.condition, node.condition)) {
- trueGuard = _conditionInfo.trueGuard;
- falseGuard = _conditionInfo.falseGuard;
- _variables.recordConditionalDiscard(source, node,
- ConditionalDiscard(trueGuard, falseGuard, _conditionInfo.isPure));
+ trueGuard = _conditionInfo!.trueGuard;
+ falseGuard = _conditionInfo!.falseGuard;
+ _variables!.recordConditionalDiscard(source, node,
+ ConditionalDiscard(trueGuard, falseGuard, _conditionInfo!.isPure));
}
- DecoratedType thenType;
- DecoratedType elseType;
+ DecoratedType? thenType;
+ DecoratedType? elseType;
// Post-dominators diverge as we branch in the conditional.
// Note: we don't have to create a scope for each branch because they can't
// define variables.
_postDominatedLocals.doScoped(action: () {
- _flowAnalysis.conditional_thenBegin(node.condition, node);
+ _flowAnalysis!.conditional_thenBegin(node.condition, node);
if (trueGuard != null) {
_guards.add(trueGuard);
}
try {
thenType = _dispatch(node.thenExpression);
if (trueGuard != null) {
- thenType = thenType
- .withNode(_nullabilityNodeForGLB(node, thenType.node, trueGuard));
+ thenType = thenType!.withNode(
+ _nullabilityNodeForGLB(node, thenType!.node!, trueGuard));
}
} finally {
if (trueGuard != null) {
_guards.removeLast();
}
}
- _flowAnalysis.conditional_elseBegin(node.thenExpression);
+ _flowAnalysis!.conditional_elseBegin(node.thenExpression);
if (falseGuard != null) {
_guards.add(falseGuard);
}
try {
elseType = _dispatch(node.elseExpression);
if (falseGuard != null) {
- elseType = elseType.withNode(
- _nullabilityNodeForGLB(node, elseType.node, falseGuard));
+ elseType = elseType!.withNode(
+ _nullabilityNodeForGLB(node, elseType!.node!, falseGuard));
}
} finally {
if (falseGuard != null) {
_guards.removeLast();
}
}
- _flowAnalysis.conditional_end(node, node.elseExpression);
+ _flowAnalysis!.conditional_end(node, node.elseExpression);
});
var overallType = _decorateUpperOrLowerBound(
- node, node.staticType, thenType, elseType, true);
- _variables.recordDecoratedExpressionType(node, overallType);
+ node, node.staticType, thenType!, elseType!, true);
+ _variables!.recordDecoratedExpressionType(node, overallType);
return overallType;
}
@override
- DecoratedType visitConstructorDeclaration(ConstructorDeclaration node) {
+ DecoratedType? visitConstructorDeclaration(ConstructorDeclaration node) {
_fieldsNotInitializedByConstructor =
- _fieldsNotInitializedAtDeclaration.toSet();
- _dispatch(node.redirectedConstructor?.type?.typeArguments);
+ _fieldsNotInitializedAtDeclaration!.toSet();
+ _dispatch(node.redirectedConstructor?.type.typeArguments);
_handleExecutableDeclaration(
node,
- node.declaredElement,
+ node.declaredElement!,
node.metadata,
null,
node.parameters,
@@ -774,19 +774,19 @@
}
@override
- DecoratedType visitConstructorFieldInitializer(
+ DecoratedType? visitConstructorFieldInitializer(
ConstructorFieldInitializer node) {
- _fieldsNotInitializedByConstructor.remove(node.fieldName.staticElement);
+ _fieldsNotInitializedByConstructor!.remove(node.fieldName.staticElement);
_handleAssignment(node.expression,
destinationType:
- getOrComputeElementType(node, node.fieldName.staticElement));
+ getOrComputeElementType(node, node.fieldName.staticElement!));
return null;
}
@override
- DecoratedType visitContinueStatement(ContinueStatement node) {
- _flowAnalysis.handleContinue(FlowAnalysisHelper.getLabelTarget(
- node, node.label?.staticElement as LabelElement));
+ DecoratedType? visitContinueStatement(ContinueStatement node) {
+ _flowAnalysis!.handleContinue(FlowAnalysisHelper.getLabelTarget(
+ node, node.label?.staticElement as LabelElement?)!);
// Later statements no longer post-dominate the declarations because we
// exited (or, in parent scopes, conditionally exited).
// TODO(mfairhurst): don't clear post-dominators beyond the current loop.
@@ -796,15 +796,15 @@
}
@override
- DecoratedType visitDefaultFormalParameter(DefaultFormalParameter node) {
+ DecoratedType? visitDefaultFormalParameter(DefaultFormalParameter node) {
_dispatch(node.parameter);
var defaultValue = node.defaultValue;
var declaredElement = node.declaredElement;
if (defaultValue == null) {
- if (declaredElement.hasRequired) {
+ if (declaredElement!.hasRequired) {
// Nothing to do; the implicit default value of `null` will never be
// reached.
- } else if (_variables.getRequiredHint(source, node) != null) {
+ } else if (_variables!.getRequiredHint(source, node) != null) {
// Nothing to do; assume the implicit default value of `null` will never
// be reached.
} else {
@@ -817,25 +817,25 @@
// default value doesn't mean the parameter has to be nullable.
} else {
_graph.makeNullable(
- getOrComputeElementType(node, declaredElement).node,
+ getOrComputeElementType(node, declaredElement).node!,
OptionalFormalParameterOrigin(source, node));
}
}
} else {
_handleAssignment(defaultValue,
- destinationType: getOrComputeElementType(node, declaredElement),
+ destinationType: getOrComputeElementType(node, declaredElement!),
fromDefaultValue: true);
}
return null;
}
@override
- DecoratedType visitDoStatement(DoStatement node) {
- _flowAnalysis.doStatement_bodyBegin(node);
+ DecoratedType? visitDoStatement(DoStatement node) {
+ _flowAnalysis!.doStatement_bodyBegin(node);
_dispatch(node.body);
- _flowAnalysis.doStatement_conditionBegin();
+ _flowAnalysis!.doStatement_conditionBegin();
_checkExpressionNotNull(node.condition);
- _flowAnalysis.doStatement_end(node.condition);
+ _flowAnalysis!.doStatement_end(node.condition);
return null;
}
@@ -845,7 +845,7 @@
}
@override
- DecoratedType visitExpressionFunctionBody(ExpressionFunctionBody node) {
+ DecoratedType? visitExpressionFunctionBody(ExpressionFunctionBody node) {
if (_currentFunctionType == null) {
_unimplemented(
node,
@@ -853,42 +853,42 @@
'(parent is ${node.parent.runtimeType})');
}
_handleAssignment(node.expression,
- destinationType: _currentFunctionType.returnType,
+ destinationType: _currentFunctionType!.returnType,
wrapFuture: node.isAsynchronous);
return null;
}
@override
DecoratedType visitExpressionStatement(ExpressionStatement node) {
- var decoratedType = _dispatch(node.expression);
+ var decoratedType = _dispatch(node.expression)!;
_graph.connectDummy(decoratedType.node, DummyOrigin(source, node));
return decoratedType;
}
- DecoratedType visitExtensionDeclaration(ExtensionDeclaration node) {
+ DecoratedType? visitExtensionDeclaration(ExtensionDeclaration node) {
_dispatch(node.typeParameters);
_dispatch(node.extendedType);
_currentExtendedType =
- _variables.decoratedTypeAnnotation(source, node.extendedType);
+ _variables!.decoratedTypeAnnotation(source, node.extendedType);
visitClassOrMixinOrExtensionDeclaration(node);
_currentExtendedType = null;
return null;
}
@override
- DecoratedType visitExtensionOverride(ExtensionOverride node) {
+ DecoratedType? visitExtensionOverride(ExtensionOverride node) {
return _dispatch(node.argumentList.arguments.single);
}
@override
- DecoratedType visitFieldFormalParameter(FieldFormalParameter node) {
+ DecoratedType? visitFieldFormalParameter(FieldFormalParameter node) {
_dispatchList(node.metadata);
_dispatch(node.parameters);
var parameterElement = node.declaredElement as FieldFormalParameterElement;
- var parameterType = _variables.decoratedElementType(parameterElement);
- var field = parameterElement.field;
- _fieldsNotInitializedByConstructor.remove(field);
- var fieldType = _variables.decoratedElementType(field);
+ var parameterType = _variables!.decoratedElementType(parameterElement);
+ var field = parameterElement.field!;
+ _fieldsNotInitializedByConstructor!.remove(field);
+ var fieldType = _variables!.decoratedElementType(field);
var origin = FieldFormalParameterOrigin(source, node);
if (node.type == null) {
_linkDecoratedTypes(parameterType, fieldType, origin, isUnion: false);
@@ -904,21 +904,21 @@
}
@override
- DecoratedType visitForElement(ForElement node) {
+ DecoratedType? visitForElement(ForElement node) {
_handleForLoopParts(node, node.forLoopParts, node.body,
(body) => _handleCollectionElement(body as CollectionElement));
return null;
}
@override
- DecoratedType visitForStatement(ForStatement node) {
+ DecoratedType? visitForStatement(ForStatement node) {
_handleForLoopParts(
node, node.forLoopParts, node.body, (body) => _dispatch(body));
return null;
}
@override
- DecoratedType visitFunctionDeclaration(FunctionDeclaration node) {
+ DecoratedType? visitFunctionDeclaration(FunctionDeclaration node) {
_dispatchList(node.metadata);
_dispatch(node.returnType);
if (_flowAnalysis != null) {
@@ -929,11 +929,11 @@
try {
_elementsWrittenToInLocalFunction = {};
_postDominatedLocals = _ScopedLocalSet();
- _flowAnalysis.functionExpression_begin(node);
+ _flowAnalysis!.functionExpression_begin(node);
_dispatch(node.functionExpression);
- _flowAnalysis.functionExpression_end();
+ _flowAnalysis!.functionExpression_end();
} finally {
- for (var element in _elementsWrittenToInLocalFunction) {
+ for (var element in _elementsWrittenToInLocalFunction!) {
previousElementsWrittenToInLocalFunction?.add(element);
previousPostDominatedLocals.removeFromAllScopes(element);
}
@@ -946,7 +946,7 @@
// Initialize a new postDominator scope that contains only the parameters.
try {
_dispatch(node.functionExpression);
- _flowAnalysis.finish();
+ _flowAnalysis!.finish();
} finally {
_flowAnalysis = null;
_assignedVariables = null;
@@ -973,19 +973,19 @@
}
@override
- DecoratedType visitFunctionExpression(FunctionExpression node) {
+ DecoratedType? visitFunctionExpression(FunctionExpression node) {
// TODO(mfairhurst): enable edge builder "_insideFunction" hard edge tests.
_dispatch(node.parameters);
_dispatch(node.typeParameters);
if (node.parent is! FunctionDeclaration) {
- _flowAnalysis.functionExpression_begin(node);
+ _flowAnalysis!.functionExpression_begin(node);
}
_addParametersToFlowAnalysis(node.parameters);
var previousFunction = _currentFunctionExpression;
var previousFunctionType = _currentFunctionType;
_currentFunctionExpression = node;
_currentFunctionType =
- _variables.decoratedElementType(node.declaredElement);
+ _variables!.decoratedElementType(node.declaredElement!);
var previousPostDominatedLocals = _postDominatedLocals;
var previousElementsWrittenToInLocalFunction =
_elementsWrittenToInLocalFunction;
@@ -995,14 +995,14 @@
}
_postDominatedLocals = _ScopedLocalSet();
_postDominatedLocals.doScoped(
- elements: node.declaredElement.parameters,
+ elements: node.declaredElement!.parameters,
action: () => _dispatch(node.body));
- _variables.recordDecoratedExpressionType(node, _currentFunctionType);
+ _variables!.recordDecoratedExpressionType(node, _currentFunctionType);
return _currentFunctionType;
} finally {
if (node.parent is! FunctionDeclaration) {
- _flowAnalysis.functionExpression_end();
- for (var element in _elementsWrittenToInLocalFunction) {
+ _flowAnalysis!.functionExpression_end();
+ for (var element in _elementsWrittenToInLocalFunction!) {
previousElementsWrittenToInLocalFunction?.add(element);
previousPostDominatedLocals.removeFromAllScopes(element);
}
@@ -1016,13 +1016,13 @@
}
@override
- DecoratedType visitFunctionExpressionInvocation(
+ DecoratedType? visitFunctionExpressionInvocation(
FunctionExpressionInvocation node) {
final argumentList = node.argumentList;
final typeArguments = node.typeArguments;
_dispatch(typeArguments);
DecoratedType calleeType = _checkExpressionNotNull(node.function);
- DecoratedType result;
+ DecoratedType? result;
if (calleeType.type is FunctionType) {
result = _handleInvocationArguments(node, argumentList.arguments,
typeArguments, node.typeArgumentTypes, calleeType, null,
@@ -1036,17 +1036,17 @@
}
@override
- DecoratedType visitIfElement(IfElement node) {
- _flowAnalysis.ifStatement_conditionBegin();
+ DecoratedType? visitIfElement(IfElement node) {
+ _flowAnalysis!.ifStatement_conditionBegin();
_checkExpressionNotNull(node.condition);
- _flowAnalysis.ifStatement_thenBegin(node.condition, node);
- NullabilityNode trueGuard;
- NullabilityNode falseGuard;
+ _flowAnalysis!.ifStatement_thenBegin(node.condition, node);
+ NullabilityNode? trueGuard;
+ NullabilityNode? falseGuard;
if (identical(_conditionInfo?.condition, node.condition)) {
- trueGuard = _conditionInfo.trueGuard;
- falseGuard = _conditionInfo.falseGuard;
- _variables.recordConditionalDiscard(source, node,
- ConditionalDiscard(trueGuard, falseGuard, _conditionInfo.isPure));
+ trueGuard = _conditionInfo!.trueGuard;
+ falseGuard = _conditionInfo!.falseGuard;
+ _variables!.recordConditionalDiscard(source, node,
+ ConditionalDiscard(trueGuard, falseGuard, _conditionInfo!.isPure));
}
if (trueGuard != null) {
_guards.add(trueGuard);
@@ -1060,7 +1060,7 @@
}
}
if (node.elseElement != null) {
- _flowAnalysis.ifStatement_elseBegin();
+ _flowAnalysis!.ifStatement_elseBegin();
if (falseGuard != null) {
_guards.add(falseGuard);
}
@@ -1073,27 +1073,27 @@
}
}
}
- _flowAnalysis.ifStatement_end(node.elseElement != null);
+ _flowAnalysis!.ifStatement_end(node.elseElement != null);
return null;
}
@override
- DecoratedType visitIfStatement(IfStatement node) {
- _flowAnalysis.ifStatement_conditionBegin();
+ DecoratedType? visitIfStatement(IfStatement node) {
+ _flowAnalysis!.ifStatement_conditionBegin();
_checkExpressionNotNull(node.condition);
- NullabilityNode trueGuard;
- NullabilityNode falseGuard;
+ NullabilityNode? trueGuard;
+ NullabilityNode? falseGuard;
if (identical(_conditionInfo?.condition, node.condition)) {
- trueGuard = _conditionInfo.trueGuard;
- falseGuard = _conditionInfo.falseGuard;
- _variables.recordConditionalDiscard(source, node,
- ConditionalDiscard(trueGuard, falseGuard, _conditionInfo.isPure));
+ trueGuard = _conditionInfo!.trueGuard;
+ falseGuard = _conditionInfo!.falseGuard;
+ _variables!.recordConditionalDiscard(source, node,
+ ConditionalDiscard(trueGuard, falseGuard, _conditionInfo!.isPure));
}
if (trueGuard != null) {
_guards.add(trueGuard);
}
try {
- _flowAnalysis.ifStatement_thenBegin(node.condition, node);
+ _flowAnalysis!.ifStatement_thenBegin(node.condition, node);
// We branched, so create a new scope for post-dominators.
_postDominatedLocals.doScoped(
action: () => _dispatch(node.thenStatement));
@@ -1108,13 +1108,13 @@
var elseStatement = node.elseStatement;
try {
if (elseStatement != null) {
- _flowAnalysis.ifStatement_elseBegin();
+ _flowAnalysis!.ifStatement_elseBegin();
// We branched, so create a new scope for post-dominators.
_postDominatedLocals.doScoped(
action: () => _dispatch(node.elseStatement));
}
} finally {
- _flowAnalysis.ifStatement_end(elseStatement != null);
+ _flowAnalysis!.ifStatement_end(elseStatement != null);
if (falseGuard != null) {
_guards.removeLast();
}
@@ -1123,8 +1123,8 @@
}
@override
- DecoratedType visitIndexExpression(IndexExpression node) {
- DecoratedType targetType;
+ DecoratedType? visitIndexExpression(IndexExpression node) {
+ DecoratedType? targetType;
var target = node.target;
if (node.isCascaded) {
targetType = _currentCascadeTargetType;
@@ -1132,7 +1132,7 @@
targetType = _checkExpressionNotNull(target);
}
var callee = getWriteOrReadElement(node);
- DecoratedType result;
+ DecoratedType? result;
if (callee == null) {
// Dynamic dispatch. The return type is `dynamic`.
// TODO(paulberry): would it be better to assume a return type of `Never`
@@ -1143,9 +1143,9 @@
targetType: targetType, targetExpression: target);
// TODO(paulberry): substitute if necessary
_handleAssignment(node.index,
- destinationType: calleeType.positionalParameters[0]);
+ destinationType: calleeType.positionalParameters![0]);
if (node.inSetterContext()) {
- result = calleeType.positionalParameters[1];
+ result = calleeType.positionalParameters![1];
} else {
result = calleeType.returnType;
}
@@ -1156,19 +1156,19 @@
@override
DecoratedType visitInstanceCreationExpression(
InstanceCreationExpression node) {
- var callee = node.constructorName.staticElement;
+ var callee = node.constructorName.staticElement!;
var typeParameters = callee.enclosingElement.typeParameters;
- Iterable<DartType> typeArgumentTypes;
+ Iterable<DartType?> typeArgumentTypes;
List<DecoratedType> decoratedTypeArguments;
var typeArguments = node.constructorName.type.typeArguments;
- List<EdgeOrigin> parameterEdgeOrigins;
+ late List<EdgeOrigin> parameterEdgeOrigins;
var target =
NullabilityNodeTarget.text('constructed type').withCodeRef(node);
if (typeArguments != null) {
_dispatch(typeArguments);
typeArgumentTypes = typeArguments.arguments.map((t) => t.type);
decoratedTypeArguments = typeArguments.arguments
- .map((t) => _variables.decoratedTypeAnnotation(source, t))
+ .map((t) => _variables!.decoratedTypeAnnotation(source, t))
.toList();
parameterEdgeOrigins = typeArguments.arguments
.map((typeAnn) => TypeParameterInstantiationOrigin(source, typeAnn))
@@ -1193,10 +1193,10 @@
}
}
- if (node.staticType.isDartCoreList &&
+ if (node.staticType!.isDartCoreList &&
callee.name == '' &&
node.argumentList.arguments.length == 1) {
- _graph.connect(_graph.always, decoratedTypeArguments[0].node,
+ _graph.connect(_graph.always, decoratedTypeArguments[0].node!,
ListLengthConstructorOrigin(source, node));
}
@@ -1208,11 +1208,11 @@
var calleeType =
getOrComputeElementType(node, callee, targetType: createdType);
for (var i = 0; i < decoratedTypeArguments.length; ++i) {
- _checkAssignment(parameterEdgeOrigins?.elementAt(i),
+ _checkAssignment(parameterEdgeOrigins.elementAt(i),
FixReasonTarget.root.typeArgument(i),
source: decoratedTypeArguments[i],
destination:
- _variables.decoratedTypeParameterBound(typeParameters[i]),
+ _variables!.decoratedTypeParameterBound(typeParameters[i])!,
hard: true);
}
_handleInvocationArguments(node, node.argumentList.arguments, typeArguments,
@@ -1228,10 +1228,10 @@
@override
DecoratedType visitIsExpression(IsExpression node) {
var expression = node.expression;
- var expressionNode = _dispatch(expression).node;
+ var expressionNode = _dispatch(expression)!.node;
var type = node.type;
_dispatch(type);
- var decoratedType = _variables.decoratedTypeAnnotation(source, type);
+ var decoratedType = _variables!.decoratedTypeAnnotation(source, type);
// The main type of the is check historically could not be nullable.
// Making it nullable could change runtime behavior.
_graph.makeNonNullable(
@@ -1242,27 +1242,27 @@
_postDominatedLocals.isReferenceInScope(expression),
trueDemonstratesNonNullIntent: expressionNode);
if (node.notOperator != null) {
- _conditionInfo = _conditionInfo.not(node);
+ _conditionInfo = _conditionInfo!.not(node);
}
if (!_assumeNonNullabilityInCasts) {
// TODO(mfairhurst): wire this to handleDowncast if we do not assume
// nullability.
assert(false);
}
- _flowAnalysis.isExpression_end(
+ _flowAnalysis!.isExpression_end(
node, expression, node.notOperator != null, decoratedType);
return _makeNonNullableBoolType(node);
}
@override
- DecoratedType visitLabel(Label node) {
+ DecoratedType? visitLabel(Label node) {
// Labels are identifiers but they don't have types so we don't need to
// visit them directly.
return null;
}
@override
- DecoratedType visitLibraryDirective(LibraryDirective node) {
+ DecoratedType? visitLibraryDirective(LibraryDirective node) {
// skip directives, but not their metadata
_dispatchList(node.metadata);
return null;
@@ -1272,18 +1272,18 @@
DecoratedType visitListLiteral(ListLiteral node) {
final previousLiteralType = _currentLiteralElementType;
try {
- var listType = node.staticType as InterfaceType;
+ var listType = node.staticType as InterfaceType?;
if (node.typeArguments == null) {
var target =
NullabilityNodeTarget.text('list element type').withCodeRef(node);
var elementType = DecoratedType.forImplicitType(
- typeProvider, listType.typeArguments[0], _graph, target);
+ typeProvider, listType!.typeArguments[0], _graph, target);
instrumentation?.implicitTypeArguments(source, node, [elementType]);
_currentLiteralElementType = elementType;
} else {
_dispatch(node.typeArguments);
- _currentLiteralElementType = _variables.decoratedTypeAnnotation(
- source, node.typeArguments.arguments[0]);
+ _currentLiteralElementType = _variables!
+ .decoratedTypeAnnotation(source, node.typeArguments!.arguments[0]);
}
node.elements.forEach(_handleCollectionElement);
return _makeNonNullLiteralType(node,
@@ -1294,7 +1294,7 @@
}
@override
- DecoratedType visitMapLiteralEntry(MapLiteralEntry node) {
+ DecoratedType? visitMapLiteralEntry(MapLiteralEntry node) {
assert(_currentMapKeyType != null);
assert(_currentMapValueType != null);
_handleAssignment(node.key, destinationType: _currentMapKeyType);
@@ -1303,16 +1303,16 @@
}
@override
- DecoratedType visitMethodDeclaration(MethodDeclaration node) {
- _handleExecutableDeclaration(node, node.declaredElement, node.metadata,
+ DecoratedType? visitMethodDeclaration(MethodDeclaration node) {
+ _handleExecutableDeclaration(node, node.declaredElement!, node.metadata,
node.returnType, node.parameters, null, node.body, null);
_dispatch(node.typeParameters);
return null;
}
@override
- DecoratedType visitMethodInvocation(MethodInvocation node) {
- DecoratedType targetType;
+ DecoratedType? visitMethodInvocation(MethodInvocation node) {
+ DecoratedType? targetType;
var target = node.target;
bool isNullAware = node.isNullAware;
var callee = node.methodName.staticElement;
@@ -1331,12 +1331,12 @@
} else {
targetType = _handleTarget(target, node.methodName.name, callee);
}
- } else if (target == null && callee.enclosingElement is ClassElement) {
+ } else if (target == null && callee!.enclosingElement is ClassElement) {
targetType = _thisOrSuper(node);
_checkThisNotNull(targetType, node);
}
- DecoratedType expressionType;
- DecoratedType calleeType;
+ DecoratedType? expressionType;
+ DecoratedType? calleeType;
if (targetType != null &&
targetType.type is FunctionType &&
node.methodName.name == 'call') {
@@ -1372,10 +1372,10 @@
expressionType = deferredProcessing(expressionType);
}
if (isNullAware) {
- expressionType = expressionType.withNode(
- NullabilityNode.forLUB(targetType.node, expressionType.node));
+ expressionType = expressionType!.withNode(
+ NullabilityNode.forLUB(targetType!.node, expressionType.node));
}
- _variables.recordDecoratedExpressionType(node, expressionType);
+ _variables!.recordDecoratedExpressionType(node, expressionType);
}
_handleArgumentErrorCheckNotNull(node);
_handleQuiverCheckNotNull(node);
@@ -1383,7 +1383,7 @@
}
@override
- DecoratedType visitMixinDeclaration(MixinDeclaration node) {
+ DecoratedType? visitMixinDeclaration(MixinDeclaration node) {
visitClassOrMixinOrExtensionDeclaration(node);
_dispatch(node.implementsClause);
_dispatch(node.onClause);
@@ -1392,14 +1392,14 @@
}
@override
- DecoratedType visitNamespaceDirective(NamespaceDirective node) {
+ DecoratedType? visitNamespaceDirective(NamespaceDirective node) {
// skip directives, but not their metadata
_dispatchList(node.metadata);
return null;
}
@override
- DecoratedType visitNode(AstNode node) {
+ DecoratedType? visitNode(AstNode node) {
for (var child in node.childEntities) {
if (child is AstNode) {
_dispatch(child);
@@ -1410,23 +1410,23 @@
@override
DecoratedType visitNullLiteral(NullLiteral node) {
- _flowAnalysis.nullLiteral(node);
+ _flowAnalysis!.nullLiteral(node);
var target = NullabilityNodeTarget.text('null literal').withCodeRef(node);
var decoratedType = DecoratedType.forImplicitType(
typeProvider, node.staticType, _graph, target);
- _graph.makeNullable(decoratedType.node, LiteralOrigin(source, node));
+ _graph.makeNullable(decoratedType.node!, LiteralOrigin(source, node));
return decoratedType;
}
@override
- DecoratedType visitParenthesizedExpression(ParenthesizedExpression node) {
+ DecoratedType? visitParenthesizedExpression(ParenthesizedExpression node) {
var result = _dispatch(node.expression);
- _flowAnalysis.parenthesizedExpression(node, node.expression);
+ _flowAnalysis!.parenthesizedExpression(node, node.expression);
return result;
}
@override
- DecoratedType visitPartOfDirective(PartOfDirective node) {
+ DecoratedType? visitPartOfDirective(PartOfDirective node) {
// skip directives, but not their metadata
_dispatchList(node.metadata);
return null;
@@ -1447,12 +1447,12 @@
} else {
var calleeType = getOrComputeElementType(node, callee,
targetType: targetType, targetExpression: operand);
- writeType = _fixNumericTypes(calleeType.returnType, node.staticType);
+ writeType = _fixNumericTypes(calleeType.returnType!, node.staticType);
}
if (operand is SimpleIdentifier) {
var element = getWriteOrReadElement(operand);
if (element is PromotableElement) {
- _flowAnalysis.write(node, element, writeType, null);
+ _flowAnalysis!.write(node, element, writeType, null);
}
}
return targetType;
@@ -1462,7 +1462,7 @@
}
@override
- DecoratedType visitPrefixedIdentifier(PrefixedIdentifier node) {
+ DecoratedType? visitPrefixedIdentifier(PrefixedIdentifier node) {
if (node.prefix.staticElement is ImportElement) {
// TODO(paulberry)
_unimplemented(node, 'PrefixedIdentifier with a prefix');
@@ -1473,17 +1473,17 @@
}
@override
- DecoratedType visitPrefixExpression(PrefixExpression node) {
+ DecoratedType? visitPrefixExpression(PrefixExpression node) {
var operand = node.operand;
var targetType = _checkExpressionNotNull(operand);
var operatorType = node.operator.type;
if (operatorType == TokenType.BANG) {
- _flowAnalysis.logicalNot_end(node, operand);
+ _flowAnalysis!.logicalNot_end(node, operand);
return _makeNonNullableBoolType(node);
} else {
var callee = node.staticElement;
var isIncrementOrDecrement = operatorType.isIncrementOperator;
- DecoratedType staticType;
+ DecoratedType? staticType;
if (callee == null) {
// Dynamic dispatch. The return type is `dynamic`.
// TODO(paulberry): would it be better to assume a return type of `Never`
@@ -1493,7 +1493,8 @@
var calleeType = getOrComputeElementType(node, callee,
targetType: targetType, targetExpression: operand);
if (isIncrementOrDecrement) {
- staticType = _fixNumericTypes(calleeType.returnType, node.staticType);
+ staticType =
+ _fixNumericTypes(calleeType.returnType!, node.staticType);
} else {
staticType = _handleInvocationArguments(
node, [], null, null, calleeType, null);
@@ -1503,7 +1504,7 @@
if (operand is SimpleIdentifier) {
var element = getWriteOrReadElement(operand);
if (element is PromotableElement) {
- _flowAnalysis.write(node, element, staticType, null);
+ _flowAnalysis!.write(node, element, staticType!, null);
}
}
}
@@ -1512,16 +1513,16 @@
}
@override
- DecoratedType visitPropertyAccess(PropertyAccess node) {
+ DecoratedType? visitPropertyAccess(PropertyAccess node) {
return _handlePropertyAccess(node, node.target, node.propertyName,
node.isNullAware, node.isCascaded);
}
@override
- DecoratedType visitRedirectingConstructorInvocation(
+ DecoratedType? visitRedirectingConstructorInvocation(
RedirectingConstructorInvocation node) {
- var callee = node.staticElement;
- var calleeType = _variables.decoratedElementType(callee);
+ var callee = node.staticElement!;
+ var calleeType = _variables!.decoratedElementType(callee);
_handleInvocationArguments(
node, node.argumentList.arguments, null, null, calleeType, null);
return null;
@@ -1529,7 +1530,7 @@
@override
DecoratedType visitRethrowExpression(RethrowExpression node) {
- _flowAnalysis.handleExit();
+ _flowAnalysis!.handleExit();
var target =
NullabilityNodeTarget.text('rethrow expression').withCodeRef(node);
var nullabilityNode = NullabilityNode.forInferredType(target);
@@ -1538,10 +1539,10 @@
}
@override
- DecoratedType visitReturnStatement(ReturnStatement node) {
- DecoratedType returnType = _currentFunctionType.returnType;
- Expression returnValue = node.expression;
- var functionBody = node.thisOrAncestorOfType<FunctionBody>();
+ DecoratedType? visitReturnStatement(ReturnStatement node) {
+ DecoratedType? returnType = _currentFunctionType!.returnType;
+ Expression? returnValue = node.expression;
+ var functionBody = node.thisOrAncestorOfType<FunctionBody>()!;
if (functionBody.isGenerator) {
// Do not connect the return value to the return type.
return _dispatch(returnValue);
@@ -1553,18 +1554,18 @@
var implicitNullType = DecoratedType.forImplicitType(
typeProvider, typeProvider.nullType, _graph, target);
var origin = ImplicitNullReturnOrigin(source, node);
- _graph.makeNullable(implicitNullType.node, origin);
+ _graph.makeNullable(implicitNullType.node!, origin);
_checkAssignment(origin, FixReasonTarget.root,
source:
isAsync ? _futureOf(implicitNullType, node) : implicitNullType,
- destination: returnType,
+ destination: returnType!,
hard: false);
} else {
_handleAssignment(returnValue,
destinationType: returnType, wrapFuture: isAsync);
}
- _flowAnalysis.handleExit();
+ _flowAnalysis!.handleExit();
// Later statements no longer post-dominate the declarations because we
// exited (or, in parent scopes, conditionally exited).
// TODO(mfairhurst): don't clear post-dominators beyond the current function.
@@ -1575,25 +1576,25 @@
@override
DecoratedType visitSetOrMapLiteral(SetOrMapLiteral node) {
- var setOrMapType = node.staticType as InterfaceType;
+ var setOrMapType = node.staticType as InterfaceType?;
var typeArguments = node.typeArguments?.arguments;
if (node.isSet) {
final previousLiteralType = _currentLiteralElementType;
try {
if (typeArguments == null) {
- assert(setOrMapType.typeArguments.length == 1);
+ assert(setOrMapType!.typeArguments.length == 1);
var target =
NullabilityNodeTarget.text('set element type').withCodeRef(node);
var elementType = DecoratedType.forImplicitType(
- typeProvider, setOrMapType.typeArguments[0], _graph, target);
+ typeProvider, setOrMapType!.typeArguments[0], _graph, target);
instrumentation?.implicitTypeArguments(source, node, [elementType]);
_currentLiteralElementType = elementType;
} else {
assert(typeArguments.length == 1);
_dispatch(node.typeArguments);
_currentLiteralElementType =
- _variables.decoratedTypeAnnotation(source, typeArguments[0]);
+ _variables!.decoratedTypeAnnotation(source, typeArguments[0]);
}
node.elements.forEach(_handleCollectionElement);
return _makeNonNullLiteralType(node,
@@ -1608,11 +1609,11 @@
final previousValueType = _currentMapValueType;
try {
if (typeArguments == null) {
- assert(setOrMapType.typeArguments.length == 2);
+ assert(setOrMapType!.typeArguments.length == 2);
var targetKey =
NullabilityNodeTarget.text('map key type').withCodeRef(node);
var keyType = DecoratedType.forImplicitType(
- typeProvider, setOrMapType.typeArguments[0], _graph, targetKey);
+ typeProvider, setOrMapType!.typeArguments[0], _graph, targetKey);
_currentMapKeyType = keyType;
var targetValue =
NullabilityNodeTarget.text('map value type').withCodeRef(node);
@@ -1625,9 +1626,9 @@
assert(typeArguments.length == 2);
_dispatch(node.typeArguments);
_currentMapKeyType =
- _variables.decoratedTypeAnnotation(source, typeArguments[0]);
+ _variables!.decoratedTypeAnnotation(source, typeArguments[0]);
_currentMapValueType =
- _variables.decoratedTypeAnnotation(source, typeArguments[1]);
+ _variables!.decoratedTypeAnnotation(source, typeArguments[1]);
}
node.elements.forEach(_handleCollectionElement);
@@ -1641,27 +1642,27 @@
}
@override
- DecoratedType visitSimpleIdentifier(SimpleIdentifier node) {
- DecoratedType targetType;
- DecoratedType result;
+ DecoratedType? visitSimpleIdentifier(SimpleIdentifier node) {
+ DecoratedType? targetType;
+ DecoratedType? result;
var staticElement = getWriteOrReadElement(node);
if (staticElement is PromotableElement) {
if (!node.inDeclarationContext()) {
- var promotedType = _flowAnalysis.variableRead(node, staticElement);
+ var promotedType = _flowAnalysis!.variableRead(node, staticElement);
if (promotedType != null) return promotedType;
}
var type = getOrComputeElementType(node, staticElement);
if (!node.inDeclarationContext() &&
node.inGetterContext() &&
!_lateHintedLocals.contains(staticElement) &&
- !_flowAnalysis.isAssigned(staticElement)) {
- _graph.makeNullable(type.node, UninitializedReadOrigin(source, node));
+ !_flowAnalysis!.isAssigned(staticElement)) {
+ _graph.makeNullable(type.node!, UninitializedReadOrigin(source, node));
}
result = type;
} else if (staticElement is FunctionElement ||
staticElement is MethodElement ||
staticElement is ConstructorElement) {
- if (staticElement.enclosingElement is ClassElement) {
+ if (staticElement!.enclosingElement is ClassElement) {
targetType = _thisOrSuper(node);
}
result =
@@ -1674,7 +1675,7 @@
getOrComputeElementType(node, staticElement, targetType: targetType);
result = staticElement.isGetter
? elementType.returnType
- : elementType.positionalParameters[0];
+ : elementType.positionalParameters![0];
} else if (staticElement is TypeDefiningElement) {
result = _makeNonNullLiteralType(node);
} else if (staticElement is ExtensionElement) {
@@ -1697,15 +1698,15 @@
}
@override
- DecoratedType visitSpreadElement(SpreadElement node) {
- final spreadType = node.expression.staticType;
- DecoratedType spreadTypeDecorated;
+ DecoratedType? visitSpreadElement(SpreadElement node) {
+ final spreadType = node.expression.staticType!;
+ DecoratedType? spreadTypeDecorated;
var target =
NullabilityNodeTarget.text('spread element type').withCodeRef(node);
if (_typeSystem.isSubtypeOf(spreadType, typeProvider.mapObjectObjectType)) {
assert(_currentMapKeyType != null && _currentMapValueType != null);
final expectedType = typeProvider.mapType(
- _currentMapKeyType.type, _currentMapValueType.type);
+ _currentMapKeyType!.type!, _currentMapValueType!.type!);
final expectedDecoratedType = DecoratedType.forImplicitType(
typeProvider, expectedType, _graph, target,
typeArguments: [_currentMapKeyType, _currentMapValueType]);
@@ -1716,7 +1717,7 @@
spreadType, typeProvider.iterableDynamicType)) {
assert(_currentLiteralElementType != null);
final expectedType =
- typeProvider.iterableType(_currentLiteralElementType.type);
+ typeProvider.iterableType(_currentLiteralElementType!.type!);
final expectedDecoratedType = DecoratedType.forImplicitType(
typeProvider, expectedType, _graph, target,
typeArguments: [_currentLiteralElementType]);
@@ -1741,22 +1742,18 @@
}
@override
- DecoratedType visitSuperConstructorInvocation(
+ DecoratedType? visitSuperConstructorInvocation(
SuperConstructorInvocation node) {
- var callee = node.staticElement;
+ var callee = node.staticElement!;
var target = NullabilityNodeTarget.text('super constructor invocation')
.withCodeRef(node);
var nullabilityNode = NullabilityNode.forInferredType(target);
- var class_ = node.thisOrAncestorOfType<ClassDeclaration>();
- var decoratedSupertype = _decoratedClassHierarchy.getDecoratedSupertype(
- class_.declaredElement, callee.enclosingElement);
+ var class_ = node.thisOrAncestorOfType<ClassDeclaration>()!;
+ var decoratedSupertype = _decoratedClassHierarchy!.getDecoratedSupertype(
+ class_.declaredElement!, callee.enclosingElement);
var typeArguments = decoratedSupertype.typeArguments;
- Iterable<DartType> typeArgumentTypes;
- if (typeArguments != null) {
- typeArgumentTypes = typeArguments.map((t) => t.type);
- } else {
- typeArgumentTypes = [];
- }
+ Iterable<DartType?> typeArgumentTypes;
+ typeArgumentTypes = typeArguments.map((t) => t!.type);
var createdType = DecoratedType(callee.returnType, nullabilityNode,
typeArguments: typeArguments);
var calleeType =
@@ -1774,19 +1771,19 @@
}
@override
- DecoratedType visitSuperExpression(SuperExpression node) {
+ DecoratedType? visitSuperExpression(SuperExpression node) {
return _thisOrSuper(node);
}
@override
- DecoratedType visitSwitchStatement(SwitchStatement node) {
+ DecoratedType? visitSwitchStatement(SwitchStatement node) {
_dispatch(node.expression);
- _flowAnalysis.switchStatement_expressionEnd(node);
+ _flowAnalysis!.switchStatement_expressionEnd(node);
var hasDefault = false;
for (var member in node.members) {
_postDominatedLocals.doScoped(action: () {
var hasLabel = member.labels.isNotEmpty;
- _flowAnalysis.switchStatement_beginCase(hasLabel, node);
+ _flowAnalysis!.switchStatement_beginCase(hasLabel, node);
if (member is SwitchCase) {
_dispatch(member.expression);
} else {
@@ -1795,7 +1792,7 @@
_dispatchList(member.statements);
});
}
- _flowAnalysis.switchStatement_end(hasDefault);
+ _flowAnalysis!.switchStatement_end(hasDefault);
return null;
}
@@ -1805,7 +1802,7 @@
}
@override
- DecoratedType visitThisExpression(ThisExpression node) {
+ DecoratedType? visitThisExpression(ThisExpression node) {
return _thisOrSuper(node);
}
@@ -1813,7 +1810,7 @@
DecoratedType visitThrowExpression(ThrowExpression node) {
_dispatch(node.expression);
// TODO(paulberry): do we need to check the expression type? I think not.
- _flowAnalysis.handleExit();
+ _flowAnalysis!.handleExit();
var target =
NullabilityNodeTarget.text('throw expression').withCodeRef(node);
var nullabilityNode = NullabilityNode.forInferredType(target);
@@ -1822,33 +1819,33 @@
}
@override
- DecoratedType visitTryStatement(TryStatement node) {
+ DecoratedType? visitTryStatement(TryStatement node) {
var finallyBlock = node.finallyBlock;
if (finallyBlock != null) {
- _flowAnalysis.tryFinallyStatement_bodyBegin();
+ _flowAnalysis!.tryFinallyStatement_bodyBegin();
}
var catchClauses = node.catchClauses;
if (catchClauses.isNotEmpty) {
- _flowAnalysis.tryCatchStatement_bodyBegin();
+ _flowAnalysis!.tryCatchStatement_bodyBegin();
}
var body = node.body;
_dispatch(body);
if (catchClauses.isNotEmpty) {
- _flowAnalysis.tryCatchStatement_bodyEnd(body);
+ _flowAnalysis!.tryCatchStatement_bodyEnd(body);
_dispatchList(catchClauses);
- _flowAnalysis.tryCatchStatement_end();
+ _flowAnalysis!.tryCatchStatement_end();
}
if (finallyBlock != null) {
- _flowAnalysis.tryFinallyStatement_finallyBegin(
+ _flowAnalysis!.tryFinallyStatement_finallyBegin(
catchClauses.isNotEmpty ? node : body);
_dispatch(finallyBlock);
- _flowAnalysis.tryFinallyStatement_end();
+ _flowAnalysis!.tryFinallyStatement_end();
}
return null;
}
@override
- DecoratedType visitTypeName(TypeName typeName) {
+ DecoratedType? visitTypeName(TypeName typeName) {
try {
_typeNameNesting++;
var typeArguments = typeName.typeArguments?.arguments;
@@ -1856,9 +1853,9 @@
if (element is TypeAliasElement) {
var aliasedElement =
element.aliasedElement as GenericFunctionTypeElement;
- final typedefType = _variables.decoratedElementType(aliasedElement);
+ final typedefType = _variables!.decoratedElementType(aliasedElement);
final typeNameType =
- _variables.decoratedTypeAnnotation(source, typeName);
+ _variables!.decoratedTypeAnnotation(source, typeName);
Map<TypeParameterElement, DecoratedType> substitutions;
if (typeName.typeArguments == null) {
@@ -1868,8 +1865,8 @@
substitutions =
Map<TypeParameterElement, DecoratedType>.fromIterables(
element.typeParameters,
- typeName.typeArguments.arguments.map(
- (t) => _variables.decoratedTypeAnnotation(source, t)));
+ typeName.typeArguments!.arguments.map(
+ (t) => _variables!.decoratedTypeAnnotation(source, t)));
}
final decoratedType = typedefType.substitute(substitutions);
@@ -1877,42 +1874,34 @@
_linkDecoratedTypeParameters(decoratedType, typeNameType, origin,
isUnion: true);
_linkDecoratedTypes(
- decoratedType.returnType, typeNameType.returnType, origin,
+ decoratedType.returnType!, typeNameType.returnType, origin,
isUnion: true);
} else if (element is TypeParameterizedElement) {
if (typeArguments == null) {
var instantiatedType =
- _variables.decoratedTypeAnnotation(source, typeName);
- if (instantiatedType == null) {
- throw StateError('No type annotation for type name '
- '${typeName.toSource()}, offset=${typeName.offset}');
- }
+ _variables!.decoratedTypeAnnotation(source, typeName);
var origin = InstantiateToBoundsOrigin(source, typeName);
for (int i = 0; i < instantiatedType.typeArguments.length; i++) {
_linkDecoratedTypes(
- instantiatedType.typeArguments[i],
- _variables
+ instantiatedType.typeArguments[i]!,
+ _variables!
.decoratedTypeParameterBound(element.typeParameters[i]),
origin,
isUnion: false);
}
} else {
for (int i = 0; i < typeArguments.length; i++) {
- DecoratedType bound;
- bound = _variables
+ DecoratedType? bound;
+ bound = _variables!
.decoratedTypeParameterBound(element.typeParameters[i]);
assert(bound != null);
var argumentType =
- _variables.decoratedTypeAnnotation(source, typeArguments[i]);
- if (argumentType == null) {
- _unimplemented(typeName,
- 'No decorated type for type argument ${typeArguments[i]} ($i)');
- }
+ _variables!.decoratedTypeAnnotation(source, typeArguments[i]);
_checkAssignment(
TypeParameterInstantiationOrigin(source, typeArguments[i]),
FixReasonTarget.root,
source: argumentType,
- destination: bound,
+ destination: bound!,
hard: true);
}
}
@@ -1934,7 +1923,7 @@
}
@override
- DecoratedType visitVariableDeclarationList(VariableDeclarationList node) {
+ DecoratedType? visitVariableDeclarationList(VariableDeclarationList node) {
var parent = node.parent;
bool isTopLevel =
parent is FieldDeclaration || parent is TopLevelVariableDeclaration;
@@ -1943,23 +1932,23 @@
for (var variable in node.variables) {
_dispatchList(variable.metadata);
var initializer = variable.initializer;
- var declaredElement = variable.declaredElement;
+ var declaredElement = variable.declaredElement!;
if (isTopLevel) {
assert(_flowAnalysis == null);
_createFlowAnalysis(variable, null);
} else {
assert(_flowAnalysis != null);
if (declaredElement is PromotableElement &&
- _variables.getLateHint(source, node) != null) {
+ _variables!.getLateHint(source, node) != null) {
_lateHintedLocals.add(declaredElement);
}
}
- var type = _variables.decoratedElementType(declaredElement);
+ var type = _variables!.decoratedElementType(declaredElement);
var enclosingElement = declaredElement.enclosingElement;
if (!declaredElement.isStatic && enclosingElement is ClassElement) {
var overriddenElements = _inheritanceManager.getOverridden2(
enclosingElement,
- Name(enclosingElement.library.source.uri, declaredElement.name));
+ Name(enclosingElement.library.source.uri, declaredElement.name!));
for (var overriddenElement
in overriddenElements ?? <ExecutableElement>[]) {
_handleFieldOverriddenDeclaration(
@@ -1969,7 +1958,7 @@
var overriddenElements = _inheritanceManager.getOverridden2(
enclosingElement,
Name(enclosingElement.library.source.uri,
- declaredElement.name + '='));
+ declaredElement.name! + '='));
for (var overriddenElement
in overriddenElements ?? <ExecutableElement>[]) {
_handleFieldOverriddenDeclaration(
@@ -1979,7 +1968,7 @@
}
try {
if (declaredElement is PromotableElement) {
- _flowAnalysis.declare(declaredElement, initializer != null);
+ _flowAnalysis!.declare(declaredElement, initializer != null);
}
if (initializer == null) {
// For top level variables and static fields, we have to generate an
@@ -1988,16 +1977,16 @@
// when processing variable reads (only if flow analysis indicates
// the variable isn't definitely assigned).
if (isTopLevel &&
- _variables.getLateHint(source, node) == null &&
+ _variables!.getLateHint(source, node) == null &&
!(declaredElement is FieldElement && !declaredElement.isStatic)) {
_graph.makeNullable(
- type.node, ImplicitNullInitializerOrigin(source, node));
+ type.node!, ImplicitNullInitializerOrigin(source, node));
}
} else {
_handleAssignment(initializer, destinationType: type);
}
if (isTopLevel) {
- _flowAnalysis.finish();
+ _flowAnalysis!.finish();
}
} finally {
if (isTopLevel) {
@@ -2022,21 +2011,21 @@
}
@override
- DecoratedType visitWhileStatement(WhileStatement node) {
+ DecoratedType? visitWhileStatement(WhileStatement node) {
// Note: we do not create guards. A null check here is *very* unlikely to be
// unnecessary after analysis.
- _flowAnalysis.whileStatement_conditionBegin(node);
+ _flowAnalysis!.whileStatement_conditionBegin(node);
_checkExpressionNotNull(node.condition);
- _flowAnalysis.whileStatement_bodyBegin(node, node.condition);
+ _flowAnalysis!.whileStatement_bodyBegin(node, node.condition);
_postDominatedLocals.doScoped(action: () => _dispatch(node.body));
- _flowAnalysis.whileStatement_end();
+ _flowAnalysis!.whileStatement_end();
return null;
}
- void _addParametersToFlowAnalysis(FormalParameterList parameters) {
+ void _addParametersToFlowAnalysis(FormalParameterList? parameters) {
if (parameters != null) {
for (var parameter in parameters.parameters) {
- _flowAnalysis.declare(parameter.declaredElement, true);
+ _flowAnalysis!.declare(parameter.declaredElement!, true);
}
}
}
@@ -2046,7 +2035,7 @@
///
/// Returns the decorated type of [expression].
DecoratedType _checkExpressionNotNull(Expression expression,
- {DecoratedType sourceType}) {
+ {DecoratedType? sourceType}) {
if (_isPrefix(expression)) {
throw ArgumentError('cannot check non-nullability of a prefix');
}
@@ -2067,28 +2056,28 @@
/// Generates the appropriate edge to assert that the value of `this` is
/// non-null.
- void _checkThisNotNull(DecoratedType thisType, AstNode node) {
+ void _checkThisNotNull(DecoratedType? thisType, AstNode node) {
// `this` can only be `null` in extensions, so if we're not in an extension,
// there's nothing to do.
if (_currentExtendedType == null) return;
var origin = ImplicitThisOrigin(source, node);
var hard =
_postDominatedLocals.isInScope(_postDominatedLocals.extensionThis);
- _graph.makeNonNullable(thisType.node, origin, hard: hard, guards: _guards);
+ _graph.makeNonNullable(thisType!.node, origin, hard: hard, guards: _guards);
}
@override
- void _connect(NullabilityNode source, NullabilityNode destination,
- EdgeOrigin origin, FixReasonTarget edgeTarget,
+ void _connect(NullabilityNode? source, NullabilityNode? destination,
+ EdgeOrigin origin, FixReasonTarget? edgeTarget,
{bool hard = false, bool checkable = true}) {
- var edge = _graph.connect(source, destination, origin,
+ var edge = _graph.connect(source, destination!, origin,
hard: hard, checkable: checkable, guards: _guards);
if (origin is ExpressionChecksOrigin) {
origin.checks.edges[edgeTarget] = edge;
}
}
- void _createFlowAnalysis(Declaration node, FormalParameterList parameters) {
+ void _createFlowAnalysis(Declaration node, FormalParameterList? parameters) {
assert(_flowAnalysis == null);
assert(_assignedVariables == null);
_assignedVariables =
@@ -2096,10 +2085,10 @@
_flowAnalysis = FlowAnalysis<AstNode, Statement, Expression,
PromotableElement, DecoratedType>(
DecoratedTypeOperations(_typeSystem, _variables, _graph),
- _assignedVariables);
+ _assignedVariables!);
if (parameters != null) {
for (var parameter in parameters.parameters) {
- _flowAnalysis.declare(parameter.declaredElement, true);
+ _flowAnalysis!.declare(parameter.declaredElement!, true);
}
}
}
@@ -2119,9 +2108,9 @@
return DecoratedType(typeProvider.objectType, nullabilityNode);
}
- DecoratedType _decorateUpperOrLowerBound(AstNode astNode, DartType type,
+ DecoratedType _decorateUpperOrLowerBound(AstNode astNode, DartType? type,
DecoratedType left, DecoratedType right, bool isLUB,
- {NullabilityNode node}) {
+ {NullabilityNode? node}) {
var leftType = left.type;
var rightType = right.type;
if (leftType is TypeParameterType && leftType != type) {
@@ -2131,7 +2120,7 @@
astNode,
type,
left.substitute(
- {typeParam: _variables.decoratedTypeParameterBound(typeParam)}),
+ {typeParam: _variables!.decoratedTypeParameterBound(typeParam)}),
right,
isLUB,
node: node);
@@ -2144,20 +2133,20 @@
type,
left,
right.substitute(
- {typeParam: _variables.decoratedTypeParameterBound(typeParam)}),
+ {typeParam: _variables!.decoratedTypeParameterBound(typeParam)}),
isLUB,
node: node);
}
node ??= isLUB
? NullabilityNode.forLUB(left.node, right.node)
- : _nullabilityNodeForGLB(astNode, left.node, right.node);
+ : _nullabilityNodeForGLB(astNode, left.node!, right.node!);
- if (type.isDynamic || type.isVoid) {
+ if (type!.isDynamic || type.isVoid) {
return DecoratedType(type, node);
- } else if (leftType.isBottom) {
+ } else if (leftType!.isBottom) {
return right.withNode(node);
- } else if (rightType.isBottom) {
+ } else if (rightType!.isBottom) {
return left.withNode(node);
} else if (type is InterfaceType) {
if (type.typeArguments.isEmpty) {
@@ -2170,13 +2159,13 @@
assert(isLUB, "shouldn't be possible to get C<T> from GLB(S, null)");
return DecoratedType(type, node, typeArguments: left.typeArguments);
} else if (leftType is InterfaceType && rightType is InterfaceType) {
- List<DecoratedType> leftTypeArguments;
- List<DecoratedType> rightTypeArguments;
+ List<DecoratedType?> leftTypeArguments;
+ List<DecoratedType?> rightTypeArguments;
if (isLUB) {
- leftTypeArguments = _decoratedClassHierarchy
+ leftTypeArguments = _decoratedClassHierarchy!
.asInstanceOf(left, type.element)
.typeArguments;
- rightTypeArguments = _decoratedClassHierarchy
+ rightTypeArguments = _decoratedClassHierarchy!
.asInstanceOf(right, type.element)
.typeArguments;
} else {
@@ -2192,8 +2181,8 @@
newTypeArguments.add(_decorateUpperOrLowerBound(
astNode,
type.typeArguments[i],
- leftTypeArguments[i],
- rightTypeArguments[i],
+ leftTypeArguments[i]!,
+ rightTypeArguments[i]!,
isLUB));
}
return DecoratedType(type, node, typeArguments: newTypeArguments);
@@ -2205,7 +2194,7 @@
}
}
} else if (type is FunctionType) {
- var leftType = left.type;
+ var leftType = left.type!;
var rightType = right.type;
if (leftType.isDartCoreNull) {
assert(
@@ -2214,7 +2203,7 @@
returnType: right.returnType,
positionalParameters: right.positionalParameters,
namedParameters: right.namedParameters);
- } else if (rightType.isDartCoreNull) {
+ } else if (rightType!.isDartCoreNull) {
assert(
isLUB, "shouldn't be possible to get a function from GLB(S, null)");
return DecoratedType(type, node,
@@ -2223,26 +2212,26 @@
namedParameters: left.namedParameters);
}
if (leftType is FunctionType && rightType is FunctionType) {
- var returnType = _decorateUpperOrLowerBound(
- astNode, type.returnType, left.returnType, right.returnType, isLUB);
+ var returnType = _decorateUpperOrLowerBound(astNode, type.returnType,
+ left.returnType!, right.returnType!, isLUB);
List<DecoratedType> positionalParameters = [];
Map<String, DecoratedType> namedParameters = {};
int positionalParameterCount = 0;
for (var parameter in type.parameters) {
- DecoratedType leftParameterType;
- DecoratedType rightParameterType;
+ DecoratedType? leftParameterType;
+ DecoratedType? rightParameterType;
if (parameter.isNamed) {
- leftParameterType = left.namedParameters[parameter.name];
- rightParameterType = right.namedParameters[parameter.name];
+ leftParameterType = left.namedParameters![parameter.name];
+ rightParameterType = right.namedParameters![parameter.name];
} else {
leftParameterType =
- left.positionalParameters[positionalParameterCount];
+ left.positionalParameters![positionalParameterCount];
rightParameterType =
- right.positionalParameters[positionalParameterCount];
+ right.positionalParameters![positionalParameterCount];
positionalParameterCount++;
}
var decoratedParameterType = _decorateUpperOrLowerBound(astNode,
- parameter.type, leftParameterType, rightParameterType, !isLUB);
+ parameter.type, leftParameterType!, rightParameterType!, !isLUB);
if (parameter.isNamed) {
namedParameters[parameter.name] = decoratedParameterType;
} else {
@@ -2260,9 +2249,9 @@
'${rightType.runtimeType}');
}
} else if (type is TypeParameterType) {
- var leftType = left.type;
+ var leftType = left.type!;
var rightType = right.type;
- if (leftType.isDartCoreNull || rightType.isDartCoreNull) {
+ if (leftType.isDartCoreNull || rightType!.isDartCoreNull) {
assert(isLUB, "shouldn't be possible to get T from GLB(null, S)");
return DecoratedType(type, node);
}
@@ -2274,7 +2263,7 @@
_unimplemented(astNode, '_decorateUpperOrLowerBound');
}
- DecoratedType _dispatch(AstNode node, {bool skipNullCheckHint = false}) {
+ DecoratedType? _dispatch(AstNode? node, {bool skipNullCheckHint = false}) {
try {
var type = node?.accept(this);
if (!skipNullCheckHint && node is Expression) {
@@ -2283,7 +2272,7 @@
return type;
} catch (exception, stackTrace) {
if (listener != null) {
- listener.reportException(source, node, exception, stackTrace);
+ listener!.reportException(source, node, exception, stackTrace);
return null;
} else {
rethrow;
@@ -2291,7 +2280,7 @@
}
}
- void _dispatchList(NodeList nodeList) {
+ void _dispatchList(NodeList? nodeList) {
if (nodeList == null) return;
for (var node in nodeList) {
_dispatch(node);
@@ -2299,8 +2288,8 @@
}
DecoratedType _fixNumericTypes(
- DecoratedType decoratedType, DartType undecoratedType) {
- if (decoratedType.type.isDartCoreNum && undecoratedType.isDartCoreInt) {
+ DecoratedType decoratedType, DartType? undecoratedType) {
+ if (decoratedType.type!.isDartCoreNum && undecoratedType!.isDartCoreInt) {
// In a few cases the type computed by normal method lookup is `num`,
// but special rules kick in to cause the type to be `int` instead. If
// that is the case, we need to fix up the decorated type.
@@ -2313,16 +2302,16 @@
DecoratedType _futureOf(DecoratedType type, AstNode node) =>
DecoratedType.forImplicitType(
typeProvider,
- typeProvider.futureType(type.type),
+ typeProvider.futureType(type.type!),
_graph,
NullabilityNodeTarget.text('implicit future').withCodeRef(node),
typeArguments: [type]);
@override
- DecoratedType _getTypeParameterTypeBound(DecoratedType type) {
+ DecoratedType? _getTypeParameterTypeBound(DecoratedType type) {
// TODO(paulberry): once we've wired up flow analysis, return promoted
// bounds if applicable.
- return _variables
+ return _variables!
.decoratedTypeParameterBound((type.type as TypeParameterType).element);
}
@@ -2337,13 +2326,13 @@
if (calleeIsStatic &&
targetIsArgumentError &&
- callee.name == 'checkNotNull' &&
+ callee!.name == 'checkNotNull' &&
node.argumentList.arguments.isNotEmpty) {
var argument = node.argumentList.arguments.first;
if (argument is SimpleIdentifier &&
_postDominatedLocals.isReferenceInScope(argument)) {
var argumentType =
- _variables.decoratedElementType(argument.staticElement);
+ _variables!.decoratedElementType(argument.staticElement!);
_graph.makeNonNullable(argumentType.node,
ArgumentErrorCheckNotNullOrigin(source, argument));
}
@@ -2360,11 +2349,11 @@
/// proper time.
///
/// Set [wrapFuture] to true to handle assigning Future<flatten(T)> to R.
- DecoratedType _handleAssignment(Expression expression,
- {DecoratedType destinationType,
- AssignmentExpression assignmentExpression,
- AssignmentExpression compoundOperatorInfo,
- AssignmentExpression questionAssignNode,
+ DecoratedType? _handleAssignment(Expression? expression,
+ {DecoratedType? destinationType,
+ AssignmentExpression? assignmentExpression,
+ AssignmentExpression? compoundOperatorInfo,
+ AssignmentExpression? questionAssignNode,
bool fromDefaultValue = false,
bool wrapFuture = false,
bool sourceIsSetupCall = false}) {
@@ -2372,9 +2361,9 @@
(assignmentExpression == null) != (destinationType == null),
'Either assignmentExpression or destinationType should be supplied, '
'but not both');
- PromotableElement destinationLocalVariable;
+ PromotableElement? destinationLocalVariable;
if (destinationType == null) {
- var destinationExpression = assignmentExpression.leftHandSide;
+ var destinationExpression = assignmentExpression!.leftHandSide;
if (destinationExpression is SimpleIdentifier) {
var element = getWriteOrReadElement(destinationExpression);
if (element is PromotableElement) {
@@ -2391,19 +2380,19 @@
}
if (questionAssignNode != null) {
- _guards.add(destinationType.node);
- _flowAnalysis.ifNullExpression_rightBegin(
+ _guards.add(destinationType!.node);
+ _flowAnalysis!.ifNullExpression_rightBegin(
questionAssignNode.leftHandSide, destinationType);
}
- DecoratedType sourceType;
+ DecoratedType? sourceType;
try {
sourceType = _dispatch(expression);
if (wrapFuture) {
- sourceType = _wrapFuture(sourceType, expression);
+ sourceType = _wrapFuture(sourceType!, expression);
}
if (sourceType == null) {
throw StateError('No type computed for ${expression.runtimeType} '
- '(${expression.toSource()}) offset=${expression.offset}');
+ '(${expression!.toSource()}) offset=${expression.offset}');
}
EdgeOrigin edgeOrigin = _makeEdgeOrigin(sourceType, expression,
isSetupAssignment: sourceIsSetupCall);
@@ -2413,21 +2402,21 @@
_checkAssignment(
CompoundAssignmentOrigin(source, compoundOperatorInfo),
FixReasonTarget.root,
- source: destinationType,
+ source: destinationType!,
destination: _createNonNullableType(compoundOperatorInfo),
- hard: _shouldUseHardEdge(assignmentExpression.leftHandSide));
+ hard: _shouldUseHardEdge(assignmentExpression!.leftHandSide));
DecoratedType compoundOperatorType = getOrComputeElementType(
compoundOperatorInfo, compoundOperatorMethod,
targetType: destinationType,
targetExpression: compoundOperatorInfo.leftHandSide);
- assert(compoundOperatorType.positionalParameters.isNotEmpty);
+ assert(compoundOperatorType.positionalParameters!.isNotEmpty);
_checkAssignment(edgeOrigin, FixReasonTarget.root,
source: sourceType,
- destination: compoundOperatorType.positionalParameters[0],
- hard: _shouldUseHardEdge(expression),
+ destination: compoundOperatorType.positionalParameters![0]!,
+ hard: _shouldUseHardEdge(expression!),
sourceIsFunctionLiteral: expression is FunctionExpression);
- sourceType = _fixNumericTypes(
- compoundOperatorType.returnType, compoundOperatorInfo.staticType);
+ sourceType = _fixNumericTypes(compoundOperatorType.returnType!,
+ compoundOperatorInfo.staticType);
_checkAssignment(
CompoundAssignmentOrigin(source, compoundOperatorInfo),
FixReasonTarget.root,
@@ -2446,7 +2435,7 @@
// the return value of the `orElse` method, so that we can later connect
// it to the nullability of the value returned from the method
// invocation.
- var extraNullability = sourceType.returnType.node;
+ var extraNullability = sourceType.returnType!.node;
_deferredMethodInvocationProcessing[
transformationInfo.methodInvocation] = (methodInvocationType) {
var newNode = NullabilityNode.forInferredType(
@@ -2454,31 +2443,31 @@
'return value from ${transformationInfo.originalName}'));
var origin = IteratorMethodReturnOrigin(
source, transformationInfo.methodInvocation);
- _graph.connect(methodInvocationType.node, newNode, origin);
+ _graph.connect(methodInvocationType!.node, newNode, origin);
_graph.connect(extraNullability, newNode, origin);
return methodInvocationType.withNode(newNode);
};
} else {
- var hard = _shouldUseHardEdge(expression,
+ var hard = _shouldUseHardEdge(expression!,
isConditionallyExecuted: questionAssignNode != null);
_checkAssignment(edgeOrigin, FixReasonTarget.root,
source: sourceType,
- destination: destinationType,
+ destination: destinationType!,
hard: hard,
sourceIsFunctionLiteral: expression is FunctionExpression);
}
}
if (destinationLocalVariable != null) {
- _flowAnalysis.write(assignmentExpression, destinationLocalVariable,
+ _flowAnalysis!.write(assignmentExpression!, destinationLocalVariable,
sourceType, compoundOperatorInfo == null ? expression : null);
}
if (questionAssignNode != null) {
- _flowAnalysis.ifNullExpression_end();
+ _flowAnalysis!.ifNullExpression_end();
// a ??= b is only nullable if both a and b are nullable.
- sourceType = destinationType.withNode(_nullabilityNodeForGLB(
- questionAssignNode, sourceType.node, destinationType.node));
- _variables.recordDecoratedExpressionType(
- questionAssignNode, sourceType);
+ sourceType = destinationType!.withNode(_nullabilityNodeForGLB(
+ questionAssignNode, sourceType.node!, destinationType.node!));
+ _variables!
+ .recordDecoratedExpressionType(questionAssignNode, sourceType);
}
} finally {
if (questionAssignNode != null) {
@@ -2496,7 +2485,7 @@
return sourceType;
}
- DecoratedType _handleCollectionElement(CollectionElement element) {
+ DecoratedType? _handleCollectionElement(CollectionElement? element) {
if (element is Expression) {
assert(_currentLiteralElementType != null);
return _handleAssignment(element,
@@ -2508,12 +2497,12 @@
void _handleConstructorRedirection(
FormalParameterList parameters, ConstructorName redirectedConstructor) {
- var callee = redirectedConstructor.staticElement.declaration;
+ var callee = redirectedConstructor.staticElement!.declaration;
var redirectedClass = callee.enclosingElement;
- var calleeType = _variables.decoratedElementType(callee);
+ var calleeType = _variables!.decoratedElementType(callee);
var typeArguments = redirectedConstructor.type.typeArguments;
var typeArgumentTypes =
- typeArguments?.arguments?.map((t) => t.type)?.toList();
+ typeArguments?.arguments.map((t) => t.type).toList();
_handleInvocationArguments(
redirectedConstructor,
parameters.parameters,
@@ -2527,17 +2516,17 @@
Declaration node,
ExecutableElement declaredElement,
NodeList<Annotation> metadata,
- TypeAnnotation returnType,
- FormalParameterList parameters,
- NodeList<ConstructorInitializer> initializers,
+ TypeAnnotation? returnType,
+ FormalParameterList? parameters,
+ NodeList<ConstructorInitializer>? initializers,
FunctionBody body,
- ConstructorName redirectedConstructor) {
+ ConstructorName? redirectedConstructor) {
assert(_currentFunctionType == null);
_dispatchList(metadata);
_dispatch(returnType);
_createFlowAnalysis(node, parameters);
_dispatch(parameters);
- _currentFunctionType = _variables.decoratedElementType(declaredElement);
+ _currentFunctionType = _variables!.decoratedElementType(declaredElement);
_addParametersToFlowAnalysis(parameters);
// Push a scope of post-dominated declarations on the stack.
_postDominatedLocals.pushScope(elements: declaredElement.parameters);
@@ -2549,11 +2538,11 @@
if (declaredElement is ConstructorElement &&
!declaredElement.isFactory &&
declaredElement.redirectedConstructor == null) {
- _handleUninitializedFields(node, _fieldsNotInitializedByConstructor);
+ _handleUninitializedFields(node, _fieldsNotInitializedByConstructor!);
}
_dispatch(body);
if (redirectedConstructor != null) {
- _handleConstructorRedirection(parameters, redirectedConstructor);
+ _handleConstructorRedirection(parameters!, redirectedConstructor);
}
if (declaredElement is! ConstructorElement) {
var enclosingElement = declaredElement.enclosingElement;
@@ -2625,7 +2614,7 @@
}
}
}
- _flowAnalysis.finish();
+ _flowAnalysis!.finish();
} finally {
_flowAnalysis = null;
_assignedVariables = null;
@@ -2636,33 +2625,33 @@
void _handleExecutableOverriddenDeclaration(
Declaration node,
- TypeAnnotation returnType,
- FormalParameterList parameters,
+ TypeAnnotation? returnType,
+ FormalParameterList? parameters,
ClassElement classElement,
Element overriddenElement) {
- overriddenElement = overriddenElement.declaration;
+ overriddenElement = overriddenElement.declaration!;
var overriddenClass = overriddenElement.enclosingElement as ClassElement;
- var decoratedSupertype = _decoratedClassHierarchy.getDecoratedSupertype(
- classElement, overriddenClass);
+ var decoratedSupertype = _decoratedClassHierarchy!
+ .getDecoratedSupertype(classElement, overriddenClass);
var substitution = decoratedSupertype.asSubstitution;
if (overriddenElement is PropertyAccessorElement &&
overriddenElement.isSynthetic) {
assert(node is MethodDeclaration);
var method = node as MethodDeclaration;
var decoratedOverriddenField =
- _variables.decoratedElementType(overriddenElement.variable);
+ _variables!.decoratedElementType(overriddenElement.variable);
var overriddenFieldType =
decoratedOverriddenField.substitute(substitution);
if (method.isGetter) {
_checkAssignment(
ReturnTypeInheritanceOrigin(source, node), FixReasonTarget.root,
- source: _currentFunctionType.returnType,
+ source: _currentFunctionType!.returnType!,
destination: overriddenFieldType,
hard: true);
} else {
assert(method.isSetter);
DecoratedType currentParameterType =
- _currentFunctionType.positionalParameters.single;
+ _currentFunctionType!.positionalParameters!.single!;
DecoratedType overriddenParameterType = overriddenFieldType;
_checkAssignment(
ParameterInheritanceOrigin(source, node), FixReasonTarget.root,
@@ -2672,20 +2661,20 @@
}
} else {
var decoratedOverriddenFunctionType =
- _variables.decoratedElementType(overriddenElement);
+ _variables!.decoratedElementType(overriddenElement);
var overriddenFunctionType =
decoratedOverriddenFunctionType.substitute(substitution);
if (returnType == null) {
_linkDecoratedTypes(
- _currentFunctionType.returnType,
+ _currentFunctionType!.returnType!,
overriddenFunctionType.returnType,
ReturnTypeInheritanceOrigin(source, node),
isUnion: false);
} else {
_checkAssignment(
ReturnTypeInheritanceOrigin(source, node), FixReasonTarget.root,
- source: _currentFunctionType.returnType,
- destination: overriddenFunctionType.returnType,
+ source: _currentFunctionType!.returnType!,
+ destination: overriddenFunctionType.returnType!,
hard: true);
}
if (parameters != null) {
@@ -2697,23 +2686,23 @@
} else {
normalParameter = (parameter as DefaultFormalParameter).parameter;
}
- DecoratedType currentParameterType;
- DecoratedType overriddenParameterType;
+ DecoratedType? currentParameterType;
+ DecoratedType? overriddenParameterType;
if (parameter.isNamed) {
- var name = normalParameter.identifier.name;
- currentParameterType = _currentFunctionType.namedParameters[name];
+ var name = normalParameter.identifier!.name;
+ currentParameterType = _currentFunctionType!.namedParameters![name];
overriddenParameterType =
- overriddenFunctionType.namedParameters[name];
+ overriddenFunctionType.namedParameters![name];
} else {
if (positionalParameterCount <
- _currentFunctionType.positionalParameters.length) {
- currentParameterType = _currentFunctionType
- .positionalParameters[positionalParameterCount];
+ _currentFunctionType!.positionalParameters!.length) {
+ currentParameterType = _currentFunctionType!
+ .positionalParameters![positionalParameterCount];
}
if (positionalParameterCount <
- overriddenFunctionType.positionalParameters.length) {
+ overriddenFunctionType.positionalParameters!.length) {
overriddenParameterType = overriddenFunctionType
- .positionalParameters[positionalParameterCount];
+ .positionalParameters![positionalParameterCount];
}
positionalParameterCount++;
}
@@ -2726,7 +2715,7 @@
} else {
_checkAssignment(origin, FixReasonTarget.root,
source: overriddenParameterType,
- destination: currentParameterType,
+ destination: currentParameterType!,
hard: false,
checkable: false);
}
@@ -2741,27 +2730,28 @@
DecoratedType type,
ClassElement classElement,
Element overriddenElement) {
- overriddenElement = overriddenElement.declaration;
+ overriddenElement = overriddenElement.declaration!;
var overriddenClass = overriddenElement.enclosingElement as ClassElement;
- var decoratedSupertype = _decoratedClassHierarchy.getDecoratedSupertype(
- classElement, overriddenClass);
+ var decoratedSupertype = _decoratedClassHierarchy!
+ .getDecoratedSupertype(classElement, overriddenClass);
var substitution = decoratedSupertype.asSubstitution;
if (overriddenElement is PropertyAccessorElement) {
- DecoratedType unsubstitutedOverriddenType;
+ DecoratedType? unsubstitutedOverriddenType;
if (overriddenElement.isSynthetic) {
unsubstitutedOverriddenType =
- _variables.decoratedElementType(overriddenElement.variable);
+ _variables!.decoratedElementType(overriddenElement.variable);
} else {
if (overriddenElement.isGetter) {
unsubstitutedOverriddenType =
- _variables.decoratedElementType(overriddenElement).returnType;
+ _variables!.decoratedElementType(overriddenElement).returnType;
} else {
- unsubstitutedOverriddenType = _variables
+ unsubstitutedOverriddenType = _variables!
.decoratedElementType(overriddenElement)
- .positionalParameters[0];
+ .positionalParameters![0];
}
}
- var overriddenType = unsubstitutedOverriddenType.substitute(substitution);
+ var overriddenType =
+ unsubstitutedOverriddenType!.substitute(substitution);
if (overriddenElement.isGetter) {
_checkAssignment(
ReturnTypeInheritanceOrigin(source, node), FixReasonTarget.root,
@@ -2778,7 +2768,7 @@
}
void _handleForLoopParts(AstNode node, ForLoopParts parts, AstNode body,
- DecoratedType Function(AstNode) bodyHandler) {
+ DecoratedType? Function(AstNode) bodyHandler) {
if (parts is ForParts) {
if (parts is ForPartsWithDeclarations) {
_dispatch(parts.variables);
@@ -2789,21 +2779,21 @@
initializationType.node, DummyOrigin(source, parts));
}
}
- _flowAnalysis.for_conditionBegin(node);
+ _flowAnalysis!.for_conditionBegin(node);
if (parts.condition != null) {
- _checkExpressionNotNull(parts.condition);
+ _checkExpressionNotNull(parts.condition!);
}
- _flowAnalysis.for_bodyBegin(
- node is Statement ? node : null, parts.condition);
+ _flowAnalysis!
+ .for_bodyBegin(node is Statement ? node : null, parts.condition);
} else if (parts is ForEachParts) {
- Element lhsElement;
- DecoratedType lhsType;
+ Element? lhsElement;
+ DecoratedType? lhsType;
if (parts is ForEachPartsWithDeclaration) {
- var variableElement = parts.loopVariable.declaredElement;
- _flowAnalysis.declare(variableElement, true);
+ var variableElement = parts.loopVariable.declaredElement!;
+ _flowAnalysis!.declare(variableElement, true);
lhsElement = variableElement;
- _dispatch(parts.loopVariable?.type);
- lhsType = _variables.decoratedElementType(lhsElement);
+ _dispatch(parts.loopVariable.type);
+ lhsType = _variables!.decoratedElementType(lhsElement);
} else if (parts is ForEachPartsWithIdentifier) {
lhsElement = parts.identifier.staticElement;
lhsType = _dispatch(parts.identifier);
@@ -2812,23 +2802,23 @@
'Unexpected ForEachParts subtype: ${parts.runtimeType}');
}
var iterableType = _checkExpressionNotNull(parts.iterable);
- DecoratedType elementType;
+ DecoratedType? elementType;
if (lhsType != null) {
- var iterableTypeType = iterableType.type;
+ var iterableTypeType = iterableType.type!;
if (_typeSystem.isSubtypeOf(
iterableTypeType, typeProvider.iterableDynamicType)) {
- elementType = _decoratedClassHierarchy
+ elementType = _decoratedClassHierarchy!
.asInstanceOf(
iterableType, typeProvider.iterableDynamicType.element)
.typeArguments[0];
_checkAssignment(
ForEachVariableOrigin(source, parts), FixReasonTarget.root,
- source: elementType, destination: lhsType, hard: false);
+ source: elementType!, destination: lhsType, hard: false);
}
}
- _flowAnalysis.forEach_bodyBegin(node);
+ _flowAnalysis!.forEach_bodyBegin(node);
if (lhsElement is PromotableElement) {
- _flowAnalysis.write(node, lhsElement,
+ _flowAnalysis!.write(node, lhsElement,
elementType ?? _makeNullableDynamicType(node), null);
}
}
@@ -2839,57 +2829,57 @@
bodyHandler(body);
if (parts is ForParts) {
- _flowAnalysis.for_updaterBegin();
- for (var updater in parts.updaters ?? <Expression>[]) {
- var updaterType = _dispatch(updater);
+ _flowAnalysis!.for_updaterBegin();
+ for (var updater in parts.updaters) {
+ var updaterType = _dispatch(updater)!;
_graph.connectDummy(updaterType.node, DummyOrigin(source, updater));
}
- _flowAnalysis.for_end();
+ _flowAnalysis!.for_end();
} else {
- _flowAnalysis.forEach_end();
+ _flowAnalysis!.forEach_end();
}
});
}
- void _handleGetterSetterCorrespondence(Declaration node, ClassElement class_,
+ void _handleGetterSetterCorrespondence(Declaration node, ClassElement? class_,
PropertyAccessorElement getter, PropertyAccessorElement setter) {
- DecoratedType getType;
+ DecoratedType? getType;
if (getter.isSynthetic) {
var field = getter.variable;
- if (field == null || field.isSynthetic) return;
- getType = _variables.decoratedElementType(field);
+ if (field.isSynthetic) return;
+ getType = _variables!.decoratedElementType(field);
} else {
- getType = _variables.decoratedElementType(getter).returnType;
+ getType = _variables!.decoratedElementType(getter).returnType;
}
- DecoratedType setType;
+ DecoratedType? setType;
if (setter.isSynthetic) {
var field = setter.variable;
- if (field == null || field.isSynthetic) return;
- setType = _variables.decoratedElementType(field);
+ if (field.isSynthetic) return;
+ setType = _variables!.decoratedElementType(field);
} else {
setType =
- _variables.decoratedElementType(setter).positionalParameters.single;
+ _variables!.decoratedElementType(setter).positionalParameters!.single;
}
- Map<TypeParameterElement, DecoratedType> getterSubstitution = const {};
- Map<TypeParameterElement, DecoratedType> setterSubstitution = const {};
+ Map<TypeParameterElement, DecoratedType?> getterSubstitution = const {};
+ Map<TypeParameterElement, DecoratedType?> setterSubstitution = const {};
if (class_ != null) {
var getterClass = getter.enclosingElement as ClassElement;
if (!identical(class_, getterClass)) {
- getterSubstitution = _decoratedClassHierarchy
+ getterSubstitution = _decoratedClassHierarchy!
.getDecoratedSupertype(class_, getterClass)
.asSubstitution;
}
var setterClass = setter.enclosingElement as ClassElement;
if (!identical(class_, setterClass)) {
- setterSubstitution = _decoratedClassHierarchy
+ setterSubstitution = _decoratedClassHierarchy!
.getDecoratedSupertype(class_, setterClass)
.asSubstitution;
}
}
_checkAssignment(
GetterSetterCorrespondenceOrigin(source, node), FixReasonTarget.root,
- source: getType.substitute(getterSubstitution),
- destination: setType.substitute(setterSubstitution),
+ source: getType!.substitute(getterSubstitution),
+ destination: setType!.substitute(setterSubstitution),
hard: true);
}
@@ -2899,10 +2889,10 @@
List<DecoratedType> argumentTypes, List<EdgeOrigin> edgeOrigins) {
for (var i = 0; i < argumentTypes.length; ++i) {
_checkAssignment(
- edgeOrigins?.elementAt(i), FixReasonTarget.root.typeArgument(i),
+ edgeOrigins.elementAt(i), FixReasonTarget.root.typeArgument(i),
source: argumentTypes[i],
- destination: DecoratedTypeParameterBounds.current
- .get((type.type as FunctionType).typeFormals[i]),
+ destination: DecoratedTypeParameterBounds.current!
+ .get((type.type as FunctionType).typeFormals[i])!,
hard: true);
}
@@ -2917,20 +2907,20 @@
///
/// Returns the decorated return type of the invocation, after any necessary
/// substitutions.
- DecoratedType _handleInvocationArguments(
+ DecoratedType? _handleInvocationArguments(
AstNode node,
Iterable<AstNode> arguments,
- TypeArgumentList typeArguments,
- Iterable<DartType> typeArgumentTypes,
+ TypeArgumentList? typeArguments,
+ Iterable<DartType?>? typeArgumentTypes,
DecoratedType calleeType,
- List<TypeParameterElement> constructorTypeParameters,
- {DartType invokeType}) {
- var typeFormals = constructorTypeParameters ?? calleeType.typeFormals;
+ List<TypeParameterElement>? constructorTypeParameters,
+ {DartType? invokeType}) {
+ var typeFormals = constructorTypeParameters ?? calleeType.typeFormals!;
var target = NullabilityNodeTarget.text('invocation').withCodeRef(node);
if (typeFormals.isNotEmpty) {
if (typeArguments != null) {
var argumentTypes = typeArguments.arguments
- .map((t) => _variables.decoratedTypeAnnotation(source, t))
+ .map((t) => _variables!.decoratedTypeAnnotation(source, t))
.toList();
var origins = typeArguments.arguments
.map((typeAnnotation) =>
@@ -2945,7 +2935,7 @@
}
} else {
if (invokeType is FunctionType) {
- var argumentTypes = typeArgumentTypes
+ var argumentTypes = typeArgumentTypes!
.map((argType) => DecoratedType.forImplicitType(
typeProvider, argType, _graph, target))
.toList();
@@ -2970,47 +2960,47 @@
int i = 0;
var suppliedNamedParameters = <String>{};
for (var argument in arguments) {
- String name;
- Expression expression;
+ String? name;
+ Expression? expression;
if (argument is NamedExpression) {
name = argument.name.label.name;
expression = argument.expression;
} else if (argument is FormalParameter) {
if (argument.isNamed) {
- name = argument.identifier.name;
+ name = argument.identifier!.name;
}
expression = argument.identifier;
} else {
expression = argument as Expression;
}
- DecoratedType parameterType;
+ DecoratedType? parameterType;
if (name != null) {
- parameterType = calleeType.namedParameters[name];
+ parameterType = calleeType.namedParameters![name];
if (parameterType == null) {
// TODO(paulberry)
_unimplemented(expression, 'Missing type for named parameter');
}
suppliedNamedParameters.add(name);
} else {
- if (calleeType.positionalParameters.length <= i) {
+ if (calleeType.positionalParameters!.length <= i) {
// TODO(paulberry)
_unimplemented(node, 'Missing positional parameter at $i');
}
- parameterType = calleeType.positionalParameters[i++];
+ parameterType = calleeType.positionalParameters![i++];
}
_handleAssignment(expression, destinationType: parameterType);
}
// Any parameters not supplied must be optional.
- for (var entry in calleeType.namedParameters.entries) {
+ for (var entry in calleeType.namedParameters!.entries) {
if (suppliedNamedParameters.contains(entry.key)) continue;
- entry.value.node.recordNamedParameterNotSupplied(
+ entry.value!.node!.recordNamedParameterNotSupplied(
_guards, _graph, NamedParameterNotSuppliedOrigin(source, node));
}
return calleeType.returnType;
}
- DecoratedType _handleNullCheckHint(
- Expression expression, DecoratedType type) {
+ DecoratedType? _handleNullCheckHint(
+ Expression expression, DecoratedType? type) {
// Sometimes we think we're looking at an expression but we're really not
// because we're inside a type name. If this happens, ignore trailing
// `/*!*/`s because they're not expression null check hints, they're type
@@ -3023,16 +3013,16 @@
}
var hint = _nullCheckHints[token] = getPostfixHint(token);
if (hint != null && hint.kind == HintCommentKind.bang) {
- _variables.recordNullCheckHint(source, expression, hint);
- return type.withNode(_graph.never);
+ _variables!.recordNullCheckHint(source, expression, hint);
+ return type!.withNode(_graph.never);
} else {
return type;
}
}
- DecoratedType _handlePropertyAccess(Expression node, Expression target,
+ DecoratedType? _handlePropertyAccess(Expression node, Expression? target,
SimpleIdentifier propertyName, bool isNullAware, bool isCascaded) {
- DecoratedType targetType;
+ DecoratedType? targetType;
var callee = getWriteOrReadElement(propertyName);
bool calleeIsStatic = callee is ExecutableElement && callee.isStatic;
if (isCascaded) {
@@ -3046,7 +3036,7 @@
} else {
targetType = _handleTarget(target, propertyName.name, callee);
}
- DecoratedType calleeType;
+ DecoratedType? calleeType;
if (targetType != null &&
targetType.type is FunctionType &&
propertyName.name == 'call') {
@@ -3070,17 +3060,17 @@
}
if (propertyName.inSetterContext()) {
if (isNullAware) {
- _conditionalNodes[node] = targetType.node;
+ _conditionalNodes[node] = targetType!.node;
}
- return calleeType.positionalParameters[0];
+ return calleeType.positionalParameters![0];
} else {
var expressionType = callee is PropertyAccessorElement
? calleeType.returnType
: calleeType;
if (isNullAware) {
- expressionType = expressionType.withNode(
- NullabilityNode.forLUB(targetType.node, expressionType.node));
- _variables.recordDecoratedExpressionType(node, expressionType);
+ expressionType = expressionType!.withNode(
+ NullabilityNode.forLUB(targetType!.node, expressionType.node));
+ _variables!.recordDecoratedExpressionType(node, expressionType);
}
return expressionType;
}
@@ -3092,7 +3082,7 @@
/// [`checkNotNull`]: https://pub.dev/documentation/quiver/latest/quiver.check/checkNotNull.html
void _handleQuiverCheckNotNull(MethodInvocation node) {
var callee = node.methodName.staticElement;
- var calleeUri = callee?.library?.source?.uri;
+ var calleeUri = callee?.library?.source.uri;
var isQuiverCheckNull = callee?.name == 'checkNotNull' &&
calleeUri != null &&
calleeUri.scheme == 'package' &&
@@ -3103,31 +3093,32 @@
if (argument is SimpleIdentifier &&
_postDominatedLocals.isReferenceInScope(argument)) {
var argumentType =
- _variables.decoratedElementType(argument.staticElement);
+ _variables!.decoratedElementType(argument.staticElement!);
_graph.makeNonNullable(
argumentType.node, QuiverCheckNotNullOrigin(source, argument));
}
}
}
- DecoratedType _handleTarget(Expression target, String name, Element callee) {
+ DecoratedType? _handleTarget(
+ Expression? target, String name, Element? callee) {
if (isDeclaredOnObject(name)) {
return _dispatch(target);
} else if ((callee is MethodElement || callee is PropertyAccessorElement) &&
- callee.enclosingElement is ExtensionElement) {
+ callee!.enclosingElement is ExtensionElement) {
// Extension methods can be called on a `null` target, when the `on` type
// of the extension is nullable. Note: we don't need to check whether the
// target type is assignable to the extended type; that is done in
// [getOrComputeElementType].
return _dispatch(target);
} else {
- return _checkExpressionNotNull(target);
+ return _checkExpressionNotNull(target!);
}
}
- void _handleUninitializedFields(AstNode node, Set<FieldElement> fields) {
+ void _handleUninitializedFields(AstNode node, Set<FieldElement?> fields) {
for (var field in fields) {
- _graph.makeNullable(_variables.decoratedElementType(field).node,
+ _graph.makeNullable(_variables!.decoratedElementType(field!).node!,
FieldNotInitializedOrigin(source, node));
}
}
@@ -3141,7 +3132,7 @@
if (grandParent is MethodInvocation) {
var enclosingInvocation = grandParent.methodName;
if (enclosingInvocation.name == 'setUp') {
- var uri = enclosingInvocation.staticElement.library?.source?.uri;
+ var uri = enclosingInvocation.staticElement!.library?.source.uri;
if (uri != null &&
uri.scheme == 'package' &&
uri.path.startsWith('test_core/')) {
@@ -3153,7 +3144,7 @@
return false;
}
- bool _isPrefix(Expression e) =>
+ bool _isPrefix(Expression? e) =>
e is SimpleIdentifier && e.staticElement is PrefixElement;
bool _isUntypedParameter(NormalFormalParameter parameter) {
@@ -3167,59 +3158,61 @@
}
void _linkDecoratedTypeParameters(
- DecoratedType x, DecoratedType y, EdgeOrigin origin,
+ DecoratedType x, DecoratedType? y, EdgeOrigin origin,
{bool isUnion = true}) {
for (int i = 0;
- i < x.positionalParameters.length && i < y.positionalParameters.length;
+ i < x.positionalParameters!.length &&
+ i < y!.positionalParameters!.length;
i++) {
_linkDecoratedTypes(
- x.positionalParameters[i], y.positionalParameters[i], origin,
+ x.positionalParameters![i]!, y.positionalParameters![i], origin,
isUnion: isUnion);
}
- for (var entry in x.namedParameters.entries) {
- var superParameterType = y.namedParameters[entry.key];
+ for (var entry in x.namedParameters!.entries) {
+ var superParameterType = y!.namedParameters![entry.key];
if (superParameterType != null) {
- _linkDecoratedTypes(entry.value, y.namedParameters[entry.key], origin,
+ _linkDecoratedTypes(entry.value!, y.namedParameters![entry.key], origin,
isUnion: isUnion);
}
}
}
- void _linkDecoratedTypes(DecoratedType x, DecoratedType y, EdgeOrigin origin,
+ void _linkDecoratedTypes(DecoratedType x, DecoratedType? y, EdgeOrigin origin,
{bool isUnion = true}) {
if (isUnion) {
- _graph.union(x.node, y.node, origin);
+ _graph.union(x.node!, y!.node!, origin);
} else {
- _graph.connect(x.node, y.node, origin, hard: true);
+ _graph.connect(x.node, y!.node!, origin, hard: true);
}
_linkDecoratedTypeParameters(x, y, origin, isUnion: isUnion);
for (int i = 0;
i < x.typeArguments.length && i < y.typeArguments.length;
i++) {
- _linkDecoratedTypes(x.typeArguments[i], y.typeArguments[i], origin,
+ _linkDecoratedTypes(x.typeArguments[i]!, y.typeArguments[i], origin,
isUnion: isUnion);
}
if (x.returnType != null && y.returnType != null) {
- _linkDecoratedTypes(x.returnType, y.returnType, origin, isUnion: isUnion);
+ _linkDecoratedTypes(x.returnType!, y.returnType, origin,
+ isUnion: isUnion);
}
}
- EdgeOrigin _makeEdgeOrigin(DecoratedType sourceType, Expression expression,
+ EdgeOrigin _makeEdgeOrigin(DecoratedType sourceType, Expression? expression,
{bool isSetupAssignment = false}) {
- if (sourceType.type.isDynamic) {
+ if (sourceType.type!.isDynamic) {
return DynamicAssignmentOrigin(source, expression);
} else {
ExpressionChecksOrigin expressionChecksOrigin = ExpressionChecksOrigin(
source, expression, ExpressionChecks(),
isSetupAssignment: isSetupAssignment);
- _variables.recordExpressionChecks(
- source, expression, expressionChecksOrigin);
+ _variables!
+ .recordExpressionChecks(source, expression!, expressionChecksOrigin);
return expressionChecksOrigin;
}
}
DecoratedType _makeNonNullableBoolType(Expression expression) {
- assert(expression.staticType.isDartCoreBool);
+ assert(expression.staticType!.isDartCoreBool);
var target =
NullabilityNodeTarget.text('expression').withCodeRef(expression);
var nullabilityNode = NullabilityNode.forInferredType(target);
@@ -3229,7 +3222,7 @@
}
DecoratedType _makeNonNullLiteralType(Expression expression,
- {List<DecoratedType> typeArguments = const []}) {
+ {List<DecoratedType?> typeArguments = const []}) {
var target =
NullabilityNodeTarget.text('expression').withCodeRef(expression);
var nullabilityNode = NullabilityNode.forInferredType(target);
@@ -3245,7 +3238,7 @@
var decoratedType = DecoratedType.forImplicitType(
typeProvider, typeProvider.dynamicType, _graph, target);
_graph.makeNullable(
- decoratedType.node, AlwaysNullableTypeOrigin(source, astNode, false));
+ decoratedType.node!, AlwaysNullableTypeOrigin(source, astNode, false));
return decoratedType;
}
@@ -3254,7 +3247,7 @@
var decoratedType = DecoratedType.forImplicitType(
typeProvider, typeProvider.voidType, _graph, target);
_graph.makeNullable(
- decoratedType.node, AlwaysNullableTypeOrigin(source, astNode, true));
+ decoratedType.node!, AlwaysNullableTypeOrigin(source, astNode, true));
return decoratedType;
}
@@ -3299,7 +3292,7 @@
_postDominatedLocals.isReferenceInScope(expression);
}
- DecoratedType _thisOrSuper(Expression node) {
+ DecoratedType? _thisOrSuper(Expression node) {
if (_currentClassOrExtension == null) {
return null;
}
@@ -3335,8 +3328,7 @@
}
}
- @alwaysThrows
- void _unimplemented(AstNode node, String message) {
+ Never _unimplemented(AstNode? node, String message) {
StringBuffer buffer = StringBuffer();
buffer.write(message);
if (node != null) {
@@ -3344,9 +3336,9 @@
buffer.write(' in "');
buffer.write(node.toSource());
buffer.write('" on line ');
- buffer.write(unit.lineInfo.getLocation(node.offset).lineNumber);
+ buffer.write(unit.lineInfo!.getLocation(node.offset).lineNumber);
buffer.write(' of "');
- buffer.write(unit.declaredElement.source.fullName);
+ buffer.write(unit.declaredElement!.source.fullName);
buffer.write('"');
}
throw UnimplementedError(buffer.toString());
@@ -3355,10 +3347,10 @@
/// Produce Future<flatten(T)> for some T, however, we would like to merely
/// upcast T to that type if possible, skipping the flatten when not
/// necessary.
- DecoratedType _wrapFuture(DecoratedType type, AstNode node) {
- var dartType = type.type;
+ DecoratedType _wrapFuture(DecoratedType type, AstNode? node) {
+ var dartType = type.type!;
if (dartType.isDartCoreNull || dartType.isBottom) {
- return _futureOf(type, node);
+ return _futureOf(type, node!);
}
if (dartType is InterfaceType &&
@@ -3367,26 +3359,26 @@
if (typeArguments.length == 1) {
// Wrapping FutureOr<T?1>?2 should produce Future<T?3>, where either 1
// or 2 being nullable causes 3 to become nullable.
- var typeArgument = typeArguments[0];
+ var typeArgument = typeArguments[0]!;
return _futureOf(
typeArgument
.withNode(NullabilityNode.forLUB(typeArgument.node, type.node)),
- node);
+ node!);
}
}
if (_typeSystem.isSubtypeOf(dartType, typeProvider.futureDynamicType)) {
- return _decoratedClassHierarchy.asInstanceOf(
- type, typeProvider.futureDynamicType.element);
+ return _decoratedClassHierarchy!
+ .asInstanceOf(type, typeProvider.futureDynamicType.element);
}
- return _futureOf(type, node);
+ return _futureOf(type, node!);
}
/// If the [node] is the finishing identifier of an assignment, return its
/// "writeElement", otherwise return its "staticElement", which might be
/// thought as the "readElement".
- static Element getWriteOrReadElement(AstNode node) {
+ static Element? getWriteOrReadElement(AstNode node) {
var writeElement = _getWriteElement(node);
if (writeElement != null) {
return writeElement;
@@ -3405,7 +3397,7 @@
/// return the corresponding "writeElement", which is the local variable,
/// the setter referenced with a [SimpleIdentifier] or a [PropertyAccess],
/// or the `[]=` operator.
- static Element _getWriteElement(AstNode node) {
+ static Element? _getWriteElement(AstNode node) {
var parent = node.parent;
if (parent is AssignmentExpression && parent.leftHandSide == node) {
return parent.writeElement;
@@ -3432,7 +3424,7 @@
mixin _AssignmentChecker {
TypeProvider get typeProvider;
- DecoratedClassHierarchy get _decoratedClassHierarchy;
+ DecoratedClassHierarchy? get _decoratedClassHierarchy;
TypeSystem get _typeSystem;
@@ -3442,14 +3434,13 @@
/// [sourceIsFunctionLiteral] indicates whether the source of the assignment
/// is a function literal expression.
void _checkAssignment(EdgeOrigin origin, FixReasonTarget edgeTarget,
- {@required DecoratedType source,
- @required DecoratedType destination,
- @required bool hard,
+ {required DecoratedType source,
+ required DecoratedType destination,
+ required bool hard,
bool checkable = true,
bool sourceIsFunctionLiteral = false}) {
- assert(origin != null);
- var sourceType = source.type;
- var destinationType = destination.type;
+ var sourceType = source.type!;
+ var destinationType = destination.type!;
if (!_typeSystem.isSubtypeOf(sourceType, destinationType)) {
// Not a proper upcast assignment.
if (_typeSystem.isSubtypeOf(destinationType, sourceType)) {
@@ -3481,12 +3472,12 @@
/// edges between them. [sourceIsFunctionLiteral] indicates whether the
/// source of the assignment is a function literal expression.
void _checkAssignment_recursion(EdgeOrigin origin, FixReasonTarget edgeTarget,
- {@required DecoratedType source,
- @required DecoratedType destination,
+ {required DecoratedType source,
+ required DecoratedType destination,
bool sourceIsFunctionLiteral = false,
bool hard = false}) {
- var sourceType = source.type;
- var destinationType = destination.type;
+ var sourceType = source.type!;
+ var destinationType = destination.type!;
assert(_typeSystem.isSubtypeOf(sourceType, destinationType));
if (destinationType.isDartAsyncFutureOr) {
var s1 = destination.typeArguments[0];
@@ -3524,22 +3515,22 @@
// Combining these, we have that S0 <: S1, contradicting our assumption.
// So the RHS of the "or" is redundant, and we can simplify to:
// - S0 <: S1.
- var s0 = source.typeArguments[0];
+ var s0 = source.typeArguments[0]!;
_checkAssignment(origin, edgeTarget.yieldedType,
- source: s0, destination: s1, hard: false);
+ source: s0, destination: s1!, hard: false);
return;
}
// (From the subtyping spec):
// if T1 is FutureOr<S1> then T0 <: T1 iff any of the following hold:
// - either T0 <: Future<S1>
if (_typeSystem.isSubtypeOf(
- sourceType, typeProvider.futureType(s1.type))) {
+ sourceType, typeProvider.futureType(s1!.type!))) {
// E.g. FutureOr<int> = (... as Future<int>)
// This is handled by the InterfaceType logic below, since we treat
// FutureOr as a supertype of Future.
}
// - or T0 <: S1
- else if (_typeSystem.isSubtypeOf(sourceType, s1.type)) {
+ else if (_typeSystem.isSubtypeOf(sourceType, s1.type!)) {
// E.g. FutureOr<int> = (... as int)
_checkAssignment_recursion(origin, edgeTarget.yieldedType,
source: source, destination: s1);
@@ -3568,7 +3559,7 @@
// Effectively this is an assignment from the type parameter's bound to
// the destination type.
_checkAssignment(origin, edgeTarget,
- source: _getTypeParameterTypeBound(source),
+ source: _getTypeParameterTypeBound(source)!,
destination: destination,
hard: false);
return;
@@ -3581,14 +3572,14 @@
// equivalent to dynamic for subtyping purposes.
} else if (sourceType is InterfaceType &&
destinationType is InterfaceType) {
- var rewrittenSource = _decoratedClassHierarchy.asInstanceOf(
- source, destinationType.element);
+ var rewrittenSource = _decoratedClassHierarchy!
+ .asInstanceOf(source, destinationType.element);
assert(rewrittenSource.typeArguments.length ==
destination.typeArguments.length);
for (int i = 0; i < rewrittenSource.typeArguments.length; i++) {
_checkAssignment(origin, edgeTarget.typeArgument(i),
- source: rewrittenSource.typeArguments[i],
- destination: destination.typeArguments[i],
+ source: rewrittenSource.typeArguments[i]!,
+ destination: destination.typeArguments[i]!,
hard: hard,
checkable: false);
}
@@ -3598,8 +3589,8 @@
// function literal has a non-nullable return type (e.g. by inserting null
// checks into the function literal).
_checkAssignment(origin, edgeTarget.returnType,
- source: source.returnType,
- destination: destination.returnType,
+ source: source.returnType!,
+ destination: destination.returnType!,
hard: sourceIsFunctionLiteral,
checkable: false);
if (source.typeArguments.isNotEmpty ||
@@ -3607,21 +3598,21 @@
throw UnimplementedError('TODO(paulberry)');
}
for (int i = 0;
- i < source.positionalParameters.length &&
- i < destination.positionalParameters.length;
+ i < source.positionalParameters!.length &&
+ i < destination.positionalParameters!.length;
i++) {
// Note: source and destination are swapped due to contravariance.
_checkAssignment(origin, edgeTarget.positionalParameter(i),
- source: destination.positionalParameters[i],
- destination: source.positionalParameters[i],
+ source: destination.positionalParameters![i]!,
+ destination: source.positionalParameters![i]!,
hard: false,
checkable: false);
}
- for (var entry in destination.namedParameters.entries) {
+ for (var entry in destination.namedParameters!.entries) {
// Note: source and destination are swapped due to contravariance.
_checkAssignment(origin, edgeTarget.namedParameter(entry.key),
- source: entry.value,
- destination: source.namedParameters[entry.key],
+ source: entry.value!,
+ destination: source.namedParameters![entry.key]!,
hard: false,
checkable: false);
}
@@ -3636,18 +3627,18 @@
}
void _checkDowncast(EdgeOrigin origin,
- {@required DecoratedType source,
- @required DecoratedType destination,
- @required bool hard}) {
- var destinationType = destination.type;
- assert(_typeSystem.isSubtypeOf(destinationType, source.type));
+ {required DecoratedType source,
+ required DecoratedType destination,
+ required bool hard}) {
+ var destinationType = destination.type!;
+ assert(_typeSystem.isSubtypeOf(destinationType, source.type!));
// Nullability should narrow to maintain subtype relationship.
_connect(source.node, destination.node, origin, FixReasonTarget.root,
hard: hard);
- if (source.type.isDynamic ||
- source.type.isDartCoreObject ||
- source.type.isVoid) {
+ if (source.type!.isDynamic ||
+ source.type!.isDartCoreObject ||
+ source.type!.isVoid) {
if (destinationType is InterfaceType) {
for (final param in destinationType.element.typeParameters) {
assert(param.bound == null,
@@ -3670,41 +3661,41 @@
// Assume an assignment to the type parameter's bound.
_checkAssignment(origin, FixReasonTarget.root,
source: source,
- destination: _getTypeParameterTypeBound(destination),
+ destination: _getTypeParameterTypeBound(destination)!,
hard: false);
} else if (destinationType == source.type) {
// Nothing to do.
return;
}
- } else if (source.type.isDartAsyncFutureOr) {
- if (destination.type.isDartAsyncFuture) {
+ } else if (source.type!.isDartAsyncFutureOr) {
+ if (destination.type!.isDartAsyncFuture) {
// FutureOr<T?> is nullable, so the Future<T> should be nullable too.
- _connect(source.typeArguments[0].node, destination.node, origin,
+ _connect(source.typeArguments[0]!.node, destination.node, origin,
FixReasonTarget.root.yieldedType,
hard: hard);
_checkDowncast(origin,
- source: source.typeArguments[0],
- destination: destination.typeArguments[0],
+ source: source.typeArguments[0]!,
+ destination: destination.typeArguments[0]!,
hard: false);
- } else if (destination.type.isDartAsyncFutureOr) {
+ } else if (destination.type!.isDartAsyncFutureOr) {
_checkDowncast(origin,
- source: source.typeArguments[0],
- destination: destination.typeArguments[0],
+ source: source.typeArguments[0]!,
+ destination: destination.typeArguments[0]!,
hard: false);
} else {
_checkDowncast(origin,
- source: source.typeArguments[0],
+ source: source.typeArguments[0]!,
destination: destination,
hard: false);
}
} else if (destinationType is InterfaceType) {
if (source.type is InterfaceType) {
- final target = _decoratedClassHierarchy.asInstanceOf(
- destination, source.type.element as ClassElement);
+ final target = _decoratedClassHierarchy!
+ .asInstanceOf(destination, source.type!.element as ClassElement?);
for (var i = 0; i < source.typeArguments.length; ++i) {
_checkDowncast(origin,
- source: source.typeArguments[i],
- destination: target.typeArguments[i],
+ source: source.typeArguments[i]!,
+ destination: target.typeArguments[i]!,
hard: false);
}
} else {
@@ -3712,7 +3703,7 @@
'downcasting from ${source.type.runtimeType} to interface type');
}
} else if (destinationType is FunctionType) {
- if (source.type.isDartCoreFunction) {
+ if (source.type!.isDartCoreFunction) {
// Nothing else to do.
return;
}
@@ -3724,12 +3715,12 @@
}
}
- void _connect(NullabilityNode source, NullabilityNode destination,
+ void _connect(NullabilityNode? source, NullabilityNode? destination,
EdgeOrigin origin, FixReasonTarget edgeTarget,
{bool hard = false, bool checkable = true});
/// Given a [type] representing a type parameter, retrieves the type's bound.
- DecoratedType _getTypeParameterTypeBound(DecoratedType type);
+ DecoratedType? _getTypeParameterTypeBound(DecoratedType type);
}
/// Information about a binary expression whose boolean value could possibly
@@ -3746,26 +3737,26 @@
final bool isPure;
/// Indicates whether the intents postdominate the intent node declarations.
- final bool postDominatingIntent;
+ final bool? postDominatingIntent;
/// If not `null`, the [NullabilityNode] that would need to be nullable in
/// order for [condition] to evaluate to `true`.
- final NullabilityNode trueGuard;
+ final NullabilityNode? trueGuard;
/// If not `null`, the [NullabilityNode] that would need to be nullable in
/// order for [condition] to evaluate to `false`.
- final NullabilityNode falseGuard;
+ final NullabilityNode? falseGuard;
/// If not `null`, the [NullabilityNode] that should be asserted to have
/// non-null intent if [condition] is asserted to be `true`.
- final NullabilityNode trueDemonstratesNonNullIntent;
+ final NullabilityNode? trueDemonstratesNonNullIntent;
/// If not `null`, the [NullabilityNode] that should be asserted to have
/// non-null intent if [condition] is asserted to be `false`.
- final NullabilityNode falseDemonstratesNonNullIntent;
+ final NullabilityNode? falseDemonstratesNonNullIntent;
_ConditionInfo(this.condition,
- {@required this.isPure,
+ {required this.isPure,
this.postDominatingIntent,
this.trueGuard,
this.falseGuard,
@@ -3785,7 +3776,7 @@
/// A [ScopedSet] specific to the [Element]s of locals/parameters.
///
/// Contains helpers for dealing with expressions as if they were elements.
-class _ScopedLocalSet extends ScopedSet<Element> {
+class _ScopedLocalSet extends ScopedSet<Element?> {
/// The synthetic element we use as a stand-in for `this` when analyzing
/// extension methods.
Element get extensionThis => DynamicElementImpl.instance;
@@ -3797,7 +3788,7 @@
/// Returns the element referenced directly by [expression], if any; otherwise
/// returns `null`.
- Element referencedElement(Expression expression) {
+ Element? referencedElement(Expression expression) {
expression = expression.unParenthesized;
if (expression is SimpleIdentifier) {
return expression.staticElement;
diff --git a/pkg/nnbd_migration/lib/src/edge_origin.dart b/pkg/nnbd_migration/lib/src/edge_origin.dart
index cff0b7b..2c2643f 100644
--- a/pkg/nnbd_migration/lib/src/edge_origin.dart
+++ b/pkg/nnbd_migration/lib/src/edge_origin.dart
@@ -50,7 +50,7 @@
/// it is the `dynamic` type).
final bool isVoid;
- AlwaysNullableTypeOrigin(Source source, AstNode node, this.isVoid)
+ AlwaysNullableTypeOrigin(Source? source, AstNode node, this.isVoid)
: super(source, node);
AlwaysNullableTypeOrigin.forElement(Element element, this.isVoid)
@@ -75,7 +75,7 @@
/// this class is used for the edge connecting the type of f's `i` parameter to
/// `never`, due to the `checkNotNull` call proclaiming that `i` is not `null`.
class ArgumentErrorCheckNotNullOrigin extends EdgeOrigin {
- ArgumentErrorCheckNotNullOrigin(Source source, SimpleIdentifier node)
+ ArgumentErrorCheckNotNullOrigin(Source? source, SimpleIdentifier node)
: super(source, node);
@override
@@ -88,7 +88,7 @@
/// An edge origin used for edges that originated because of a tear-off of
/// `call` on a function type.
class CallTearOffOrigin extends EdgeOrigin {
- CallTearOffOrigin(Source source, AstNode node) : super(source, node);
+ CallTearOffOrigin(Source? source, AstNode node) : super(source, node);
@override
String get description => 'tear-off of .call';
@@ -100,7 +100,7 @@
/// Edge origin resulting from the use of a value on the LHS of a compound
/// assignment.
class CompoundAssignmentOrigin extends EdgeOrigin {
- CompoundAssignmentOrigin(Source source, AssignmentExpression node)
+ CompoundAssignmentOrigin(Source? source, AssignmentExpression node)
: super(source, node);
@override
@@ -110,13 +110,13 @@
EdgeOriginKind get kind => EdgeOriginKind.compoundAssignment;
@override
- AssignmentExpression get node => super.node as AssignmentExpression;
+ AssignmentExpression? get node => super.node as AssignmentExpression?;
}
/// Edge origin resulting from the use of an element which does not affect the
/// nullability graph in other ways.
class DummyOrigin extends EdgeOrigin {
- DummyOrigin(Source source, AstNode node) : super(source, node);
+ DummyOrigin(Source? source, AstNode node) : super(source, node);
@override
String get description => 'dummy';
@@ -128,7 +128,7 @@
/// An edge origin used for edges that originated because of an assignment
/// involving a value with a dynamic type.
class DynamicAssignmentOrigin extends EdgeOrigin {
- DynamicAssignmentOrigin(Source source, AstNode node) : super(source, node);
+ DynamicAssignmentOrigin(Source? source, AstNode? node) : super(source, node);
@override
String get description => 'assignment of dynamic value';
@@ -142,13 +142,13 @@
/// tool to create the edge.
abstract class EdgeOrigin extends EdgeOriginInfo {
@override
- final Source source;
+ final Source? source;
@override
- final AstNode node;
+ final AstNode? node;
@override
- final Element element;
+ final Element? element;
EdgeOrigin(this.source, this.node) : element = null;
@@ -158,9 +158,9 @@
/// Retrieves the location in the source code that caused this edge to be
/// created, or `null` if unknown.
- CodeReference get codeReference {
+ CodeReference? get codeReference {
if (node != null) {
- return CodeReference.fromAstNode(node);
+ return CodeReference.fromAstNode(node!);
}
return null;
}
@@ -172,7 +172,7 @@
/// An edge origin used for edges that originated because of a reference to an
/// enum value, which cannot be null.
class EnumValueOrigin extends EdgeOrigin {
- EnumValueOrigin(Source source, AstNode node) : super(source, node);
+ EnumValueOrigin(Source? source, AstNode node) : super(source, node);
@override
String get description => 'non-nullable enum value';
@@ -184,7 +184,7 @@
/// Edge origin resulting from the relationship between a field formal parameter
/// and the corresponding field.
class FieldFormalParameterOrigin extends EdgeOrigin {
- FieldFormalParameterOrigin(Source source, FieldFormalParameter node)
+ FieldFormalParameterOrigin(Source? source, FieldFormalParameter node)
: super(source, node);
@override
@@ -201,7 +201,7 @@
/// that failed to initialize the field (or the class, if the constructor is
/// synthetic).
class FieldNotInitializedOrigin extends EdgeOrigin {
- FieldNotInitializedOrigin(Source source, AstNode node) : super(source, node);
+ FieldNotInitializedOrigin(Source? source, AstNode node) : super(source, node);
@override
String get description => 'field not initialized';
@@ -220,7 +220,8 @@
/// this class is used for the edge connecting the type of `l`'s `int` type
/// parameter to the type of `i`.
class ForEachVariableOrigin extends EdgeOrigin {
- ForEachVariableOrigin(Source source, ForEachParts node) : super(source, node);
+ ForEachVariableOrigin(Source? source, ForEachParts node)
+ : super(source, node);
@override
String get description => 'variable in "for each" loop';
@@ -231,7 +232,7 @@
/// Edge origin resulting from the relationship between a getter and a setter.
class GetterSetterCorrespondenceOrigin extends EdgeOrigin {
- GetterSetterCorrespondenceOrigin(Source source, AstNode node)
+ GetterSetterCorrespondenceOrigin(Source? source, AstNode node)
: super(source, node);
@override
@@ -251,7 +252,7 @@
/// `x` and `y` are nullable, due to the fact that the `int` in the return type
/// is the greatest lower bound of the two other `int`s.
class GreatestLowerBoundOrigin extends EdgeOrigin {
- GreatestLowerBoundOrigin(Source source, AstNode node) : super(source, node);
+ GreatestLowerBoundOrigin(Source? source, AstNode? node) : super(source, node);
@override
String get description => 'greatest lower bound';
@@ -262,7 +263,7 @@
/// Edge origin resulting from the presence of a `??` operator.
class IfNullOrigin extends EdgeOrigin {
- IfNullOrigin(Source source, AstNode node) : super(source, node);
+ IfNullOrigin(Source? source, AstNode node) : super(source, node);
@override
String get description => 'if-null operator';
@@ -285,7 +286,7 @@
/// between the implicit constructor for `D` and the explicit constructor for
/// `C`.
class ImplicitMixinSuperCallOrigin extends EdgeOrigin {
- ImplicitMixinSuperCallOrigin(Source source, ClassTypeAlias node)
+ ImplicitMixinSuperCallOrigin(Source? source, ClassTypeAlias node)
: super(source, node);
@override
@@ -298,7 +299,7 @@
/// Edge origin resulting from the implicit assignment of `null` to a top level
/// variable or field that lacks an initializer.
class ImplicitNullInitializerOrigin extends EdgeOrigin {
- ImplicitNullInitializerOrigin(Source source, AstNode node)
+ ImplicitNullInitializerOrigin(Source? source, AstNode node)
: super(source, node);
@override
@@ -311,7 +312,7 @@
/// Edge origin resulting from a `return;` statement which implicitly returns
/// `null`.
class ImplicitNullReturnOrigin extends EdgeOrigin {
- ImplicitNullReturnOrigin(Source source, ReturnStatement node)
+ ImplicitNullReturnOrigin(Source? source, ReturnStatement node)
: super(source, node);
@override
@@ -321,13 +322,13 @@
EdgeOriginKind get kind => EdgeOriginKind.implicitNullReturn;
@override
- ReturnStatement get node => super.node as ReturnStatement;
+ ReturnStatement? get node => super.node as ReturnStatement?;
}
/// Edge origin used for edges that arise from an implicit use of `this`, e.g.
/// during a method call from an extension.
class ImplicitThisOrigin extends EdgeOrigin {
- ImplicitThisOrigin(Source source, AstNode node) : super(source, node);
+ ImplicitThisOrigin(Source? source, AstNode node) : super(source, node);
@override
String get description => 'implicit use of `this`';
@@ -339,7 +340,7 @@
/// Edge origin resulting from the inference of a type parameter, which
/// can affects the nullability of that type parameter's bound.
class InferredTypeParameterInstantiationOrigin extends EdgeOrigin {
- InferredTypeParameterInstantiationOrigin(Source source, AstNode node)
+ InferredTypeParameterInstantiationOrigin(Source? source, AstNode node)
: super(source, node);
@override
@@ -352,7 +353,7 @@
/// An edge origin used for edges that originated because of an instance
/// creation expression.
class InstanceCreationOrigin extends EdgeOrigin {
- InstanceCreationOrigin(Source source, AstNode node) : super(source, node);
+ InstanceCreationOrigin(Source? source, AstNode node) : super(source, node);
@override
String get description => 'instance creation';
@@ -370,7 +371,8 @@
/// this class is used for the edge connecting the type of x's type parameter
/// with the type bound in the declaration of C.
class InstantiateToBoundsOrigin extends EdgeOrigin {
- InstantiateToBoundsOrigin(Source source, TypeName node) : super(source, node);
+ InstantiateToBoundsOrigin(Source? source, TypeName node)
+ : super(source, node);
@override
String get description => 'type instantiated to bounds';
@@ -385,7 +387,7 @@
/// Before the migration, there was no way to say `is int?`, and therefore,
/// `is int` should migrate to non-null int.
class IsCheckMainTypeOrigin extends EdgeOrigin {
- IsCheckMainTypeOrigin(Source source, TypeAnnotation node)
+ IsCheckMainTypeOrigin(Source? source, TypeAnnotation node)
: super(source, node);
@override
@@ -398,7 +400,8 @@
/// An edge origin used for the return type of an iterator method that might be
/// changed into an extension method from package:collection.
class IteratorMethodReturnOrigin extends EdgeOrigin {
- IteratorMethodReturnOrigin(Source source, AstNode node) : super(source, node);
+ IteratorMethodReturnOrigin(Source? source, AstNode node)
+ : super(source, node);
@override
String get description =>
@@ -411,7 +414,7 @@
/// An edge origin used for the type argument of a list constructor that
/// specified an initial length, because that type argument must be nullable.
class ListLengthConstructorOrigin extends EdgeOrigin {
- ListLengthConstructorOrigin(Source source, AstNode node)
+ ListLengthConstructorOrigin(Source? source, AstNode node)
: super(source, node);
@override
@@ -424,7 +427,7 @@
/// An edge origin used for edges that originated because a literal expression
/// has a known nullability.
class LiteralOrigin extends EdgeOrigin {
- LiteralOrigin(Source source, AstNode node) : super(source, node);
+ LiteralOrigin(Source? source, AstNode node) : super(source, node);
@override
String get description => 'literal expression';
@@ -446,7 +449,7 @@
/// parameter, due to the fact that the call to `f` implicitly passes a null
/// value for `i`.
class NamedParameterNotSuppliedOrigin extends EdgeOrigin {
- NamedParameterNotSuppliedOrigin(Source source, AstNode node)
+ NamedParameterNotSuppliedOrigin(Source? source, AstNode node)
: super(source, node);
@override
@@ -459,7 +462,7 @@
/// Edge origin for the nullability of an expression that whose type is fixed by
/// the language definition to be non-nullable `bool`.
class NonNullableBoolTypeOrigin extends EdgeOrigin {
- NonNullableBoolTypeOrigin(Source source, AstNode node) : super(source, node);
+ NonNullableBoolTypeOrigin(Source? source, AstNode node) : super(source, node);
@override
String get description => 'non-null boolean expression';
@@ -471,7 +474,7 @@
/// Edge origin resulting from the class/superclass relationship for a class
/// whose superclass is implicitly `Object`.
class NonNullableObjectSuperclass extends EdgeOrigin {
- NonNullableObjectSuperclass(Source source, AstNode node)
+ NonNullableObjectSuperclass(Source? source, AstNode node)
: super(source, node);
@override
@@ -484,7 +487,7 @@
/// Edge origin resulting from the usage of a value in a circumstance that
/// requires it to be non-nullable
class NonNullableUsageOrigin extends EdgeOrigin {
- NonNullableUsageOrigin(Source source, AstNode node) : super(source, node);
+ NonNullableUsageOrigin(Source? source, AstNode node) : super(source, node);
@override
String get description => 'value cannot be null';
@@ -503,7 +506,7 @@
/// this class is used for the edge connecting the type of f's `i` parameter to
/// `never`, due to the assert statement proclaiming that `i` is not `null`.
class NonNullAssertionOrigin extends EdgeOrigin {
- NonNullAssertionOrigin(Source source, Assertion node) : super(source, node);
+ NonNullAssertionOrigin(Source? source, Assertion node) : super(source, node);
@override
String get description => 'value asserted to be non-null';
@@ -525,7 +528,7 @@
/// non-nullable.
final bool isNullable;
- NullabilityCommentOrigin(Source source, AstNode node, this.isNullable)
+ NullabilityCommentOrigin(Source? source, AstNode node, this.isNullable)
: assert(node is TypeAnnotation ||
node is FunctionTypedFormalParameter ||
(node is FieldFormalParameter && node.parameters != null)),
@@ -547,7 +550,7 @@
/// this class is used for the edge connecting `always` to the type of f's `i`
/// parameter, due to the fact that `i` is optional and has no initializer.
class OptionalFormalParameterOrigin extends EdgeOrigin {
- OptionalFormalParameterOrigin(Source source, DefaultFormalParameter node)
+ OptionalFormalParameterOrigin(Source? source, DefaultFormalParameter node)
: super(source, node);
@override
@@ -560,7 +563,8 @@
/// Edge origin resulting from an inheritance relationship between two method
/// parameters.
class ParameterInheritanceOrigin extends EdgeOrigin {
- ParameterInheritanceOrigin(Source source, AstNode node) : super(source, node);
+ ParameterInheritanceOrigin(Source? source, AstNode node)
+ : super(source, node);
@override
String get description => 'function parameter override';
@@ -581,7 +585,7 @@
/// this class is used for the edge connecting the type of f's `i` parameter to
/// `never`, due to the `checkNotNull` call proclaiming that `i` is not `null`.
class QuiverCheckNotNullOrigin extends EdgeOrigin {
- QuiverCheckNotNullOrigin(Source source, SimpleIdentifier node)
+ QuiverCheckNotNullOrigin(Source? source, SimpleIdentifier node)
: super(source, node);
@override
@@ -594,7 +598,7 @@
/// Edge origin resulting from an inheritance relationship between two method
/// return types.
class ReturnTypeInheritanceOrigin extends EdgeOrigin {
- ReturnTypeInheritanceOrigin(Source source, AstNode node)
+ ReturnTypeInheritanceOrigin(Source? source, AstNode node)
: super(source, node);
@override
@@ -608,7 +612,7 @@
/// directive. The type of such parameters is fixed by the language as
/// non-nullable `StackTrace`.
class StackTraceTypeOrigin extends EdgeOrigin {
- StackTraceTypeOrigin(Source source, AstNode node) : super(source, node);
+ StackTraceTypeOrigin(Source? source, AstNode? node) : super(source, node);
@override
String get description => 'stack trace variable is nullable';
@@ -623,7 +627,7 @@
/// expression in question is `super`.
final bool isThis;
- ThisOrSuperOrigin(Source source, AstNode node, this.isThis)
+ ThisOrSuperOrigin(Source? source, AstNode node, this.isThis)
: super(source, node);
@override
@@ -637,7 +641,7 @@
/// An edge origin used for edges that originated from the type of a `throw` or
/// `rethrow`.
class ThrowOrigin extends EdgeOrigin {
- ThrowOrigin(Source source, AstNode node) : super(source, node);
+ ThrowOrigin(Source? source, AstNode node) : super(source, node);
@override
String get description =>
@@ -654,7 +658,7 @@
/// unioned with references to the nodes referring to source code. The origin of
/// those union edges will be [TypedefReferenceOrigin].
class TypedefReferenceOrigin extends EdgeOrigin {
- TypedefReferenceOrigin(Source source, TypeName node) : super(source, node);
+ TypedefReferenceOrigin(Source? source, TypeName node) : super(source, node);
@override
String get description => 'reference to typedef';
@@ -666,7 +670,7 @@
/// Edge origin resulting from the instantiation of a type parameter, which
/// affects the nullability of that type parameter's bound.
class TypeParameterInstantiationOrigin extends EdgeOrigin {
- TypeParameterInstantiationOrigin(Source source, TypeAnnotation node)
+ TypeParameterInstantiationOrigin(Source? source, TypeAnnotation node)
: super(source, node);
@override
@@ -676,13 +680,13 @@
EdgeOriginKind get kind => EdgeOriginKind.typeParameterInstantiation;
@override
- TypeAnnotation get node => super.node as TypeAnnotation;
+ TypeAnnotation? get node => super.node as TypeAnnotation?;
}
/// Edge origin resulting from the read of a variable that has not been
/// definitely assigned a value.
class UninitializedReadOrigin extends EdgeOrigin {
- UninitializedReadOrigin(Source source, AstNode node) : super(source, node);
+ UninitializedReadOrigin(Source? source, AstNode node) : super(source, node);
@override
String get description => 'local variable might not be initialized';
diff --git a/pkg/nnbd_migration/lib/src/edit_plan.dart b/pkg/nnbd_migration/lib/src/edit_plan.dart
index 633732f..33728b9 100644
--- a/pkg/nnbd_migration/lib/src/edit_plan.dart
+++ b/pkg/nnbd_migration/lib/src/edit_plan.dart
@@ -16,8 +16,8 @@
import 'package:nnbd_migration/nnbd_migration.dart';
import 'package:nnbd_migration/src/utilities/hint_utils.dart';
-Map<int, List<AtomicEdit>> _removeCode(
- int offset, int end, _RemovalStyle removalStyle, AtomicEditInfo info) {
+Map<int?, List<AtomicEdit>>? _removeCode(
+ int offset, int end, _RemovalStyle removalStyle, AtomicEditInfo? info) {
if (offset < end) {
// TODO(paulberry): handle preexisting comments?
switch (removalStyle) {
@@ -41,7 +41,6 @@
end: [AtomicEdit.insert(' */', info: info)]
};
}
- throw StateError('Null value for removalStyle');
} else {
return null;
}
@@ -61,7 +60,7 @@
class AtomicEdit {
/// Additional information about this edit, or `null` if no additional
/// information is available.
- final AtomicEditInfo info;
+ final AtomicEditInfo? info;
/// The number of characters that should be deleted by this edit, or `0` if no
/// characters should be deleted.
@@ -131,11 +130,11 @@
final NullabilityFixDescription description;
/// The reasons for the edit.
- final Map<FixReasonTarget, FixReasonInfo> fixReasons;
+ final Map<FixReasonTarget?, FixReasonInfo?> fixReasons;
/// If the edit is being made due to a hint, the hint in question; otherwise
/// `null`.
- final HintComment hintComment;
+ final HintComment? hintComment;
AtomicEditInfo(this.description, this.fixReasons, {this.hintComment});
}
@@ -161,7 +160,7 @@
/// plans that replace one AST node with another, this is the parent of the
/// AST node being replaced. For edit plans that insert or delete AST nodes,
/// this is the parent of the AST nodes that will be inserted or deleted.
- AstNode get parentNode;
+ AstNode? get parentNode;
}
/// Factory class for creating [EditPlan]s.
@@ -169,18 +168,18 @@
/// Indicates whether code removed by the EditPlanner should be removed by
/// commenting it out. A value of `false` means to actually delete the code
/// that is removed.
- final bool removeViaComments;
+ final bool? removeViaComments;
/// The line info for the source file being edited. This is used when
/// removing statements that fill one or more lines, so that we can remove
/// the indentation as well as the statement, and avoid leaving behind ugly
/// whitespace.
- final LineInfo lineInfo;
+ final LineInfo? lineInfo;
/// The text of the source file being edited. This is used when removing
/// code, so that we can figure out if it is safe to remove adjoining
/// whitespace.
- final String sourceText;
+ final String? sourceText;
EditPlanner(this.lineInfo, this.sourceText, {this.removeViaComments = false});
@@ -189,12 +188,12 @@
/// commented out.
NodeProducingEditPlan acceptPrefixHint(
NodeProducingEditPlan innerPlan, HintComment hint,
- {AtomicEditInfo info}) {
+ {AtomicEditInfo? info}) {
var affixPlan = innerPlan is _CommentAffixPlan
? innerPlan
: _CommentAffixPlan(innerPlan);
var changes = hint.changesToAccept(sourceText, info: info);
- assert(affixPlan.offset >= _endForChanges(changes));
+ assert(affixPlan.offset! >= _endForChanges(changes)!);
affixPlan.offset = _offsetForChanges(changes);
affixPlan._prefixChanges = changes + affixPlan._prefixChanges;
return affixPlan;
@@ -205,12 +204,12 @@
/// commented out.
NodeProducingEditPlan acceptSuffixHint(
NodeProducingEditPlan innerPlan, HintComment hint,
- {AtomicEditInfo info}) {
+ {AtomicEditInfo? info}) {
var affixPlan = innerPlan is _CommentAffixPlan
? innerPlan
: _CommentAffixPlan(innerPlan);
var changes = hint.changesToAccept(sourceText);
- assert(affixPlan.end <= _offsetForChanges(changes));
+ assert(affixPlan.end! <= _offsetForChanges(changes)!);
affixPlan.end = _endForChanges(changes);
affixPlan._postfixChanges += hint.changesToAccept(sourceText, info: info);
return affixPlan;
@@ -223,7 +222,7 @@
/// made.
NodeProducingEditPlan addBinaryPostfix(
NodeProducingEditPlan innerPlan, TokenType operator, String operand,
- {AtomicEditInfo info}) {
+ {AtomicEditInfo? info}) {
assert(innerPlan.sourceNode is Expression);
var precedence = Precedence.forTokenType(operator);
var isAssociative = precedence != Precedence.relational &&
@@ -249,7 +248,7 @@
/// of `false`.
NodeProducingEditPlan addBinaryPrefix(
String operand, TokenType operator, NodeProducingEditPlan innerPlan,
- {AtomicEditInfo info, bool allowCascade = false}) {
+ {AtomicEditInfo? info, bool allowCascade = false}) {
assert(innerPlan.sourceNode is Expression);
var precedence = Precedence.forTokenType(operator);
var isAssociative = precedence == Precedence.assignment;
@@ -272,8 +271,8 @@
/// default).
NodeProducingEditPlan addCommentPostfix(
NodeProducingEditPlan innerPlan, String comment,
- {AtomicEditInfo info, bool isInformative = false}) {
- var end = innerPlan.end;
+ {AtomicEditInfo? info, bool isInformative = false}) {
+ var end = innerPlan.end!;
return surround(innerPlan, suffix: [
AtomicEdit.insert(' ', isInformative: isInformative),
AtomicEdit.insert(comment, info: info, isInformative: isInformative),
@@ -290,7 +289,7 @@
/// Optional argument [info] contains information about why the change was
/// made.
NodeProducingEditPlan addPostfix(NodeProducingEditPlan innerPlan, String text,
- {AtomicEditInfo info}) {
+ {AtomicEditInfo? info}) {
assert(innerPlan.sourceNode is Expression);
return surround(innerPlan,
suffix: [AtomicEdit.insert(text, info: info)],
@@ -307,7 +306,7 @@
/// made.
NodeProducingEditPlan addUnaryPostfix(
NodeProducingEditPlan innerPlan, TokenType operator,
- {AtomicEditInfo info}) =>
+ {AtomicEditInfo? info}) =>
addPostfix(innerPlan, operator.lexeme, info: info);
/// Creates a new edit plan that consists of executing [innerPlan], and then
@@ -317,7 +316,7 @@
/// made.
NodeProducingEditPlan addUnaryPrefix(
TokenType operator, NodeProducingEditPlan innerPlan,
- {AtomicEditInfo info}) {
+ {AtomicEditInfo? info}) {
assert(innerPlan.sourceNode is Expression);
return surround(innerPlan,
prefix: [AtomicEdit.insert(operator.lexeme, info: info)],
@@ -330,19 +329,19 @@
///
/// Exposed so that we can substitute a mock class in unit tests.
@visibleForTesting
- PassThroughBuilder createPassThroughBuilder(AstNode node) =>
+ PassThroughBuilder createPassThroughBuilder(AstNode? node) =>
_PassThroughBuilderImpl(node);
/// Creates a new edit plan that consists of executing [innerPlan], and then
/// dropping the given nullability [hint].
NodeProducingEditPlan dropNullabilityHint(
NodeProducingEditPlan innerPlan, HintComment hint,
- {AtomicEditInfo info}) {
+ {AtomicEditInfo? info}) {
var affixPlan = innerPlan is _CommentAffixPlan
? innerPlan
: _CommentAffixPlan(innerPlan);
var changes = hint.changesToRemove(sourceText, info: info);
- assert(affixPlan.end <= _offsetForChanges(changes));
+ assert(affixPlan.end! <= _offsetForChanges(changes)!);
affixPlan.end = _endForChanges(changes);
affixPlan._postfixChanges += changes;
return affixPlan;
@@ -354,7 +353,7 @@
/// Optional argument [info] contains information about why the change was
/// made.
NodeProducingEditPlan explainNonNullable(NodeProducingEditPlan innerPlan,
- {AtomicEditInfo info}) {
+ {AtomicEditInfo? info}) {
assert(innerPlan.sourceNode is TypeAnnotation);
return surround(innerPlan,
suffix: [AtomicEdit.insert(' ', info: info, isInformative: true)]);
@@ -377,11 +376,11 @@
/// the newly created plan is finalized), so it should not be re-used by the
/// caller.
NodeProducingEditPlan extract(
- AstNode sourceNode, NodeProducingEditPlan innerPlan,
- {AtomicEditInfo infoBefore,
- AtomicEditInfo infoAfter,
+ AstNode? sourceNode, NodeProducingEditPlan innerPlan,
+ {AtomicEditInfo? infoBefore,
+ AtomicEditInfo? infoAfter,
bool alwaysDelete = false}) {
- var parent = innerPlan.sourceNode.parent;
+ var parent = innerPlan.sourceNode!.parent;
if (!identical(parent, sourceNode) && parent is ParenthesizedExpression) {
innerPlan = _ProvisionalParenEditPlan(parent, innerPlan);
}
@@ -395,7 +394,7 @@
///
/// Finalizing an [EditPlan] is a destructive operation; it should not be used
/// again after it is finalized.
- Map<int, List<AtomicEdit>> finalize(EditPlan plan) {
+ Map<int?, List<AtomicEdit>>? finalize(EditPlan plan) {
// Convert to a plan for the top level CompilationUnit.
var parent = plan.parentNode;
if (parent != null) {
@@ -413,7 +412,7 @@
/// The created edit plan should be inserted into the list of inner plans for
/// a pass-through plan targeted at the [containingNode]. See [passThrough].
EditPlan informativeMessageForToken(AstNode containingNode, Token token,
- {AtomicEditInfo info}) {
+ {AtomicEditInfo? info}) {
return _TokenChangePlan(containingNode, {
token.offset: [
AtomicEdit.delete(token.lexeme.length, info: info, isInformative: true)
@@ -438,7 +437,7 @@
/// Optional argument [info] contains information about why the change was
/// made.
NodeProducingEditPlan makeNullable(NodeProducingEditPlan innerPlan,
- {AtomicEditInfo info}) {
+ {AtomicEditInfo? info}) {
var sourceNode = innerPlan.sourceNode;
assert(sourceNode is TypeAnnotation ||
sourceNode is FunctionTypedFormalParameter ||
@@ -454,7 +453,7 @@
/// All plans in [innerPlans] will be finalized as a side effect (either
/// immediately or when the newly created plan is finalized), so they should
/// not be re-used by the caller.
- NodeProducingEditPlan passThrough(AstNode node,
+ NodeProducingEditPlan passThrough(AstNode? node,
{Iterable<EditPlan> innerPlans = const []}) {
// It's possible that some of the inner plans are nested more deeply within
// [node] than others. We want to group these inner plans together into
@@ -464,7 +463,7 @@
// [node], and each subsequent entry will correspond to a child of the
// previous.
var builderStack = [createPassThroughBuilder(node)];
- var ancestryPath = <AstNode>[];
+ var ancestryPath = <AstNode?>[];
for (var plan in innerPlans) {
// Compute the ancestryPath (the path from `plan.parentNode` up to
// `node`). Note that whereas builderStack walks stepwise down the AST,
@@ -474,7 +473,7 @@
ancestryPath.clear();
for (var parent = plan.parentNode;
!identical(parent, node);
- parent = parent.parent) {
+ parent = parent!.parent) {
ancestryPath.add(parent);
}
ancestryPath.add(node);
@@ -516,7 +515,7 @@
///
/// Optional argument [info] contains information about why the change was
/// made.
- EditPlan removeNode(AstNode sourceNode, {AtomicEditInfo info}) {
+ EditPlan removeNode(AstNode sourceNode, {AtomicEditInfo? info}) {
var result = tryRemoveNode(sourceNode, info: info);
if (result == null) {
var parent = sourceNode.parent;
@@ -540,7 +539,7 @@
/// Optional argument [info] contains information about why the change was
/// made.
EditPlan removeNodes(AstNode firstSourceNode, AstNode lastSourceNode,
- {AtomicEditInfo info}) {
+ {AtomicEditInfo? info}) {
var parent = firstSourceNode.parent;
assert(identical(lastSourceNode.parent, parent));
var sequenceNodes = _computeSequenceNodes(parent);
@@ -567,8 +566,8 @@
/// informative, or should actually be applied to the final output (the
/// default).
EditPlan removeNullAwareness(Expression sourceNode,
- {AtomicEditInfo info, bool isInformative = false}) {
- Token operator;
+ {AtomicEditInfo? info, bool isInformative = false}) {
+ Token? operator;
if (sourceNode is MethodInvocation) {
operator = sourceNode.operator;
} else if (sourceNode is PropertyAccess) {
@@ -578,9 +577,9 @@
'Tried to remove null awareness from an unexpected node type: '
'${sourceNode.runtimeType}');
}
- assert(operator.type == TokenType.QUESTION_PERIOD);
+ assert(operator!.type == TokenType.QUESTION_PERIOD);
return _TokenChangePlan(sourceNode, {
- operator.offset: [
+ operator!.offset: [
AtomicEdit.delete(1, info: info, isInformative: isInformative)
]
});
@@ -601,7 +600,7 @@
AstNode sourceNode, List<AtomicEdit> replacement,
{Precedence precedence = Precedence.primary,
bool endsInCascade = false,
- AtomicEditInfo info}) {
+ AtomicEditInfo? info}) {
assert(!replacement.any((edit) => !edit.isInsertion),
'All edits should be insertions');
return _SimpleEditPlan(sourceNode, precedence, endsInCascade, {
@@ -617,7 +616,7 @@
///
/// [parentNode] should be the innermost AST node containing [token].
EditPlan replaceToken(AstNode parentNode, Token token, String replacement,
- {AtomicEditInfo info}) {
+ {AtomicEditInfo? info}) {
return _TokenChangePlan(parentNode, {
token.offset: [AtomicEdit.replace(token.length, replacement, info: info)]
});
@@ -650,8 +649,8 @@
/// this situation, whether the final plan ends in a cascade section will be
/// determined by [innerPlan]).
NodeProducingEditPlan surround(NodeProducingEditPlan innerPlan,
- {List<AtomicEdit> prefix,
- List<AtomicEdit> suffix,
+ {List<AtomicEdit>? prefix,
+ List<AtomicEdit>? suffix,
Precedence outerPrecedence = Precedence.primary,
Precedence innerPrecedence = Precedence.none,
bool associative = false,
@@ -686,8 +685,8 @@
///
/// Optional argument [info] contains information about why the change was
/// made.
- EditPlan tryRemoveNode(AstNode sourceNode,
- {List<AstNode> sequenceNodes, AtomicEditInfo info}) {
+ EditPlan? tryRemoveNode(AstNode sourceNode,
+ {List<AstNode>? sequenceNodes, AtomicEditInfo? info}) {
var parent = sourceNode.parent;
sequenceNodes ??= _computeSequenceNodes(parent);
if (sequenceNodes == null) {
@@ -705,7 +704,7 @@
/// [offset]).
int _backAcrossWhitespace(int offset, int limit) {
assert(limit <= offset);
- return limit + sourceText.substring(limit, offset).trimRight().length;
+ return limit + sourceText!.substring(limit, offset).trimRight().length;
}
/// Walks backward through the source text, starting at [offset] and stopping
@@ -713,20 +712,20 @@
///
/// If [offset] is at the beginning of the line, it is returned unchanged.
int _backToLineStart(int offset) {
- var lineNumber = lineInfo.getLocation(offset).lineNumber;
+ var lineNumber = lineInfo!.getLocation(offset).lineNumber;
// lineNumber is one-based, but lineInfo.lineStarts expects a zero-based
// index, so we need `lineInfo.lineStarts[lineNumber - 1]`.
- return lineInfo.lineStarts[lineNumber - 1];
+ return lineInfo!.lineStarts[lineNumber - 1];
}
- int _endForChanges(Map<int, List<AtomicEdit>> changes) {
- int result;
+ int? _endForChanges(Map<int?, List<AtomicEdit>> changes) {
+ int? result;
for (var entry in changes.entries) {
var end = entry.key;
for (var edit in entry.value) {
- end += edit.length;
+ end = end! + edit.length;
}
- if (result == null || end > result) result = end;
+ if (result == null || end! > result) result = end;
}
return result;
}
@@ -737,7 +736,7 @@
/// the last entry of [ancestryStack] corresponding to the first entry of
/// [builderStack].
int _findMatchingBuilder(
- List<PassThroughBuilder> builderStack, List<AstNode> ancestryStack) {
+ List<PassThroughBuilder> builderStack, List<AstNode?> ancestryStack) {
var builderIndex = builderStack.length - 1;
while (builderIndex > 0) {
var ancestryIndex = ancestryStack.length - builderIndex - 1;
@@ -757,51 +756,51 @@
/// Does not walk further than [limit] (which should be greater than or equal
/// to [offset]).
int _forwardAcrossWhitespace(int offset, int limit) {
- return limit - sourceText.substring(offset, limit).trimLeft().length;
+ return limit - sourceText!.substring(offset, limit).trimLeft().length;
}
/// Walks forward through the source text, starting at [offset] and stopping
/// at the beginning of the next line (or at the end of the document, if this
/// line is the last line).
int _forwardToLineEnd(int offset) {
- int lineNumber = lineInfo.getLocation(offset).lineNumber;
+ int lineNumber = lineInfo!.getLocation(offset).lineNumber;
// lineNumber is one-based, so if it is equal to
// `lineInfo.lineStarts.length`, then we are on the last line.
- if (lineNumber >= lineInfo.lineStarts.length) {
- return sourceText.length;
+ if (lineNumber >= lineInfo!.lineStarts.length) {
+ return sourceText!.length;
}
// lineInfo.lineStarts expects a zero-based index, so
// `lineInfo.lineStarts[lineNumber]` gives us the beginning of the next
// line.
- return lineInfo.lineStarts[lineNumber];
+ return lineInfo!.lineStarts[lineNumber];
}
/// Determines whether the given source [offset] comes just after one of the
/// characters in [characters].
bool _isJustAfter(int offset, List<String> characters) =>
- offset > 0 && characters.contains(sourceText[offset - 1]);
+ offset > 0 && characters.contains(sourceText![offset - 1]);
/// Determines whether the given source [end] comes just before one of the
/// characters in [characters].
bool _isJustBefore(int end, List<String> characters) =>
- end < sourceText.length && characters.contains(sourceText[end]);
+ end < sourceText!.length && characters.contains(sourceText![end]);
/// Determines whether the given source [end] comes just before whitespace.
/// For the purpose of this check, the end of the file is considered
/// whitespace.
bool _isJustBeforeWhitespace(int end) =>
- end >= sourceText.length || _isWhitespaceRange(end, end + 1);
+ end >= sourceText!.length || _isWhitespaceRange(end, end + 1);
/// Determines if the characters between [offset] and [end] in the source text
/// are all whitespace characters.
bool _isWhitespaceRange(int offset, int end) {
- return sourceText.substring(offset, end).trimRight().isEmpty;
+ return sourceText!.substring(offset, end).trimRight().isEmpty;
}
- int _offsetForChanges(Map<int, List<AtomicEdit>> changes) {
- int result;
+ int? _offsetForChanges(Map<int?, List<AtomicEdit>> changes) {
+ int? result;
for (var key in changes.keys) {
- if (result == null || key < result) result = key;
+ if (result == null || key! < result) result = key;
}
return result;
}
@@ -813,7 +812,7 @@
/// maintain its child nodes. For example, [CompilationUnit] maintains its
/// directives and declarations in separate lists, so the returned list is
/// a new list containing both directives and declarations.
- static List<AstNode> _computeSequenceNodes(AstNode node) {
+ static List<AstNode>? _computeSequenceNodes(AstNode? node) {
if (node is Block) {
return node.statements;
} else if (node is ListLiteral) {
@@ -849,12 +848,12 @@
/// declaration, etc.)
abstract class NodeProducingEditPlan extends EditPlan {
/// The AST node to which the edit plan applies.
- final AstNode sourceNode;
+ final AstNode? sourceNode;
NodeProducingEditPlan._(this.sourceNode) : super._();
/// Offset just past the end of the source text affected by this plan.
- int get end => sourceNode.end;
+ int? get end => sourceNode!.end;
/// If the result of executing this [EditPlan] will be an expression,
/// indicates whether the expression will end in an unparenthesized cascade.
@@ -862,10 +861,10 @@
bool get endsInCascade;
/// Offset of the start of the source text affected by this plan.
- int get offset => sourceNode.offset;
+ int? get offset => sourceNode!.offset;
@override
- AstNode get parentNode => sourceNode.parent;
+ AstNode? get parentNode => sourceNode!.parent;
/// Determines whether the text produced by this [EditPlan] would need
/// parentheses if it were to be used as a replacement for its [sourceNode].
@@ -876,9 +875,9 @@
/// If a non-null value is provided for [cascadeSearchLimit], it is the most
/// distant ancestor that will be searched.
@visibleForTesting
- bool parensNeededFromContext(AstNode cascadeSearchLimit) {
+ bool? parensNeededFromContext(AstNode? cascadeSearchLimit) {
if (sourceNode is! Expression) return false;
- var parent = sourceNode.parent;
+ var parent = sourceNode!.parent;
return parent == null
? false
: parent
@@ -889,8 +888,8 @@
/// works even if [changes] already includes modifications at the beginning or
/// end of [sourceNode]--the parentheses are inserted outside of any
/// pre-existing changes.
- Map<int, List<AtomicEdit>> _createAddParenChanges(
- Map<int, List<AtomicEdit>> changes) {
+ Map<int?, List<AtomicEdit>>? _createAddParenChanges(
+ Map<int?, List<AtomicEdit>>? changes) {
changes ??= {};
(changes[offset] ??= []).insert(0, const AtomicEdit.insert('('));
(changes[end] ??= []).add(const AtomicEdit.insert(')'));
@@ -902,12 +901,12 @@
///
/// An [EditPlan] for which [_getChanges] has been called is considered to be
/// finalized.
- Map<int, List<AtomicEdit>> _getChanges(bool parens);
+ Map<int?, List<AtomicEdit>>? _getChanges(bool parens);
/// Determines if the text that would be produced by [EditPlan] needs to be
/// surrounded by parens, based on the context in which it will be used.
bool _parensNeeded(
- {@required Precedence threshold,
+ {required Precedence threshold,
bool associative = false,
bool allowCascade = false});
}
@@ -918,7 +917,7 @@
@visibleForTesting
abstract class PassThroughBuilder {
/// The AST node that is the parent of all the [EditPlan]s being accumulated.
- AstNode get node;
+ AstNode? get node;
/// Accumulate another edit plan.
void add(EditPlan innerPlan);
@@ -930,15 +929,15 @@
/// [EditPlan] that wraps an inner plan with optional prefix and suffix changes.
class _CommentAffixPlan extends _NestedEditPlan {
- Map<int, List<AtomicEdit>> _prefixChanges;
+ Map<int?, List<AtomicEdit>>? _prefixChanges;
- Map<int, List<AtomicEdit>> _postfixChanges;
+ Map<int?, List<AtomicEdit>>? _postfixChanges;
@override
- int offset;
+ int? offset;
@override
- int end;
+ int? end;
_CommentAffixPlan(NodeProducingEditPlan innerPlan)
: offset = innerPlan.offset,
@@ -946,7 +945,7 @@
super(innerPlan.sourceNode, innerPlan);
@override
- Map<int, List<AtomicEdit>> _getChanges(bool parens) =>
+ Map<int?, List<AtomicEdit>>? _getChanges(bool parens) =>
_prefixChanges + innerPlan._getChanges(parens) + _postfixChanges;
}
@@ -978,20 +977,20 @@
class _ExtractEditPlan extends _NestedEditPlan {
final EditPlanner _planner;
- final AtomicEditInfo _infoBefore;
+ final AtomicEditInfo? _infoBefore;
- final AtomicEditInfo _infoAfter;
+ final AtomicEditInfo? _infoAfter;
/// Whether text-to-be-removed should be removed (as opposed to commented out)
/// even when [EditPlan.removeViaComments] is true.
final bool _alwaysDelete;
- _ExtractEditPlan(AstNode sourceNode, NodeProducingEditPlan innerPlan,
+ _ExtractEditPlan(AstNode? sourceNode, NodeProducingEditPlan innerPlan,
this._planner, this._infoBefore, this._infoAfter, this._alwaysDelete)
: super(sourceNode, innerPlan);
@override
- Map<int, List<AtomicEdit>> _getChanges(bool parens) {
+ Map<int?, List<AtomicEdit>>? _getChanges(bool parens) {
// Get the inner changes. If they already have provisional parens and we
// need them, use them.
var useInnerParens = parens && innerPlan is _ProvisionalParenEditPlan;
@@ -999,7 +998,7 @@
// TODO(paulberry): don't remove comments
_RemovalStyle leadingChangeRemovalStyle;
_RemovalStyle trailingChangeRemovalStyle;
- if (_alwaysDelete || !_planner.removeViaComments) {
+ if (_alwaysDelete || !_planner.removeViaComments!) {
leadingChangeRemovalStyle = _RemovalStyle.delete;
trailingChangeRemovalStyle = _RemovalStyle.delete;
} else {
@@ -1007,10 +1006,11 @@
trailingChangeRemovalStyle = _RemovalStyle.spaceComment;
}
// Extract the inner expression.
- changes = _removeCode(
- offset, innerPlan.offset, leadingChangeRemovalStyle, _infoBefore) +
+ changes = _removeCode(offset!, innerPlan.offset!, leadingChangeRemovalStyle,
+ _infoBefore) +
changes +
- _removeCode(innerPlan.end, end, trailingChangeRemovalStyle, _infoAfter);
+ _removeCode(
+ innerPlan.end!, end!, trailingChangeRemovalStyle, _infoAfter);
// Apply parens if needed.
if (parens && !useInnerParens) {
changes = _createAddParenChanges(changes);
@@ -1027,14 +1027,14 @@
abstract class _NestedEditPlan extends NodeProducingEditPlan {
final NodeProducingEditPlan innerPlan;
- _NestedEditPlan(AstNode sourceNode, this.innerPlan) : super._(sourceNode);
+ _NestedEditPlan(AstNode? sourceNode, this.innerPlan) : super._(sourceNode);
@override
bool get endsInCascade => innerPlan.endsInCascade;
@override
bool _parensNeeded(
- {@required Precedence threshold,
+ {required Precedence threshold,
bool associative = false,
bool allowCascade = false}) =>
innerPlan._parensNeeded(
@@ -1054,13 +1054,13 @@
/// them represents a cascade section (and hence, parentheses are required).
/// If a non-null value is provided for [_cascadeSearchLimit], it is the most
/// distant ancestor that will be searched.
- final AstNode _cascadeSearchLimit;
+ final AstNode? _cascadeSearchLimit;
_ParensNeededFromContextVisitor(this._editPlan, this._cascadeSearchLimit) {
assert(_target is Expression);
}
- AstNode get _target => _editPlan.sourceNode;
+ AstNode? get _target => _editPlan.sourceNode;
@override
bool visitAsExpression(AsExpression node) {
@@ -1257,28 +1257,28 @@
class _PassThroughBuilderImpl implements PassThroughBuilder {
@override
- final AstNode node;
+ final AstNode? node;
/// The [EditPlan]s accumulated so far.
final List<EditPlan> innerPlans = [];
/// The [EditPlanner] currently being used to create this
/// [_PassThroughEditPlan].
- EditPlanner planner;
+ late EditPlanner planner;
/// Determination of whether the resulting [EditPlan] will end in a cascade,
/// or `null` if it is not yet known.
- bool endsInCascade;
+ bool? endsInCascade;
/// The set of changes aggregated together so far.
- Map<int, List<AtomicEdit>> changes;
+ Map<int?, List<AtomicEdit>>? changes;
/// If [node] is a sequence, the list of its child nodes. Otherwise `null`.
- List<AstNode> sequenceNodes;
+ List<AstNode>? sequenceNodes;
/// If [node] is a sequence that uses separators (e.g. a list literal, which
/// uses comma separators), a list of its separators. Otherwise `null`.
- List<Token> separators;
+ List<Token>? separators;
/// If [separators] is non-null, and nodes are being removed from the
/// sequence, this boolean indicates whether each node should be removed along
@@ -1331,7 +1331,7 @@
precedence = Precedence.primary;
}
return _PassThroughEditPlan._(
- node, precedence, endsInCascade ?? node.endsInCascade, changes);
+ node, precedence, endsInCascade ?? node!.endsInCascade, changes);
}
/// Starting at index [planIndex] of [innerPlans] (whose value is [plan]),
@@ -1361,7 +1361,7 @@
/// Processes an inner plan of type [NodeProducingEditPlan].
void _handleNodeProducingEditPlan(NodeProducingEditPlan innerPlan) {
- var parensNeeded = innerPlan.parensNeededFromContext(node);
+ var parensNeeded = innerPlan.parensNeededFromContext(node)!;
assert(_checkParenLogic(innerPlan, parensNeeded));
if (!parensNeeded && innerPlan is _ProvisionalParenEditPlan) {
var innerInnerPlan = innerPlan.innerPlan;
@@ -1374,7 +1374,7 @@
// Note: we use innerPlan.sourceNode.end here instead of innerPlan.end,
// because what we care about is the input grammar, so we don't want to be
// fooled by any whitespace or comments included in the innerPlan.
- if (endsInCascade == null && innerPlan.sourceNode.end == node.end) {
+ if (endsInCascade == null && innerPlan.sourceNode!.end == node!.end) {
endsInCascade = !parensNeeded && innerPlan.endsInCascade;
}
}
@@ -1393,8 +1393,8 @@
int nextRemovalOffset;
removeLeadingSeparators = separators != null &&
firstPlan.firstChildIndex != 0 &&
- lastPlan.lastChildIndex >= separators.length;
- if (planner.removeViaComments) {
+ lastPlan.lastChildIndex >= separators!.length;
+ if (planner.removeViaComments!) {
nextRemovalOffset = _removalOffset(firstPlan);
lastRemovalEnd = _removalEnd(lastPlan);
} else {
@@ -1421,15 +1421,15 @@
firstRemovalOffset = firstLineStart;
lastRemovalEnd = lastLineEnd;
}
- if (firstPlanIndex == 0 && lastPlanIndex == sequenceNodes.length - 1) {
+ if (firstPlanIndex == 0 && lastPlanIndex == sequenceNodes!.length - 1) {
// We're removing everything. Try to remove additional whitespace so
// that we're left with just `()`, `{}`, or `[]`.
var candidateFirstRemovalOffset =
- planner._backAcrossWhitespace(firstRemovalOffset, node.offset);
+ planner._backAcrossWhitespace(firstRemovalOffset, node!.offset);
if (planner
._isJustAfter(candidateFirstRemovalOffset, const ['(', '[', '{'])) {
var candidateLastRemovalEnd =
- planner._forwardAcrossWhitespace(lastRemovalEnd, node.end);
+ planner._forwardAcrossWhitespace(lastRemovalEnd, node!.end);
if (planner
._isJustBefore(candidateLastRemovalEnd, const [')', ']', '}'])) {
firstRemovalOffset = candidateFirstRemovalOffset;
@@ -1450,7 +1450,7 @@
var nextInnerPlan = innerPlans[planIndex + 1] as _RemoveEditPlan;
assert(identical(nextInnerPlan.parentNode, node));
nextRemovalOffset = _removalOffset(nextInnerPlan);
- if (planner.removeViaComments) {
+ if (planner.removeViaComments!) {
end = _removalEnd(innerPlans[planIndex] as _RemoveEditPlan);
} else {
var lineStart = planner._backToLineStart(nextRemovalOffset);
@@ -1466,7 +1466,7 @@
changes += _removeCode(
offset,
end,
- planner.removeViaComments
+ planner.removeViaComments!
? _RemovalStyle.spaceInsideComment
: _RemovalStyle.delete,
innerPlan.info);
@@ -1500,10 +1500,10 @@
int _removalEnd(_RemoveEditPlan innerPlan) {
if (separators != null &&
!removeLeadingSeparators &&
- innerPlan.lastChildIndex < separators.length) {
- return separators[innerPlan.lastChildIndex].end;
+ innerPlan.lastChildIndex < separators!.length) {
+ return separators![innerPlan.lastChildIndex].end;
} else {
- return sequenceNodes[innerPlan.lastChildIndex].end;
+ return sequenceNodes![innerPlan.lastChildIndex].end;
}
}
@@ -1511,9 +1511,9 @@
/// [innerPlan].
int _removalOffset(_RemoveEditPlan innerPlan) {
if (separators != null && removeLeadingSeparators) {
- return separators[innerPlan.firstChildIndex - 1].offset;
+ return separators![innerPlan.firstChildIndex - 1].offset;
} else {
- return sequenceNodes[innerPlan.firstChildIndex].offset;
+ return sequenceNodes![innerPlan.firstChildIndex].offset;
}
}
@@ -1536,8 +1536,8 @@
/// Compute the set of tokens used by the given [parent] node to separate its
/// [childNodes].
- static List<Token> _computeSeparators(
- AstNode parent, List<AstNode> childNodes) {
+ static List<Token>? _computeSeparators(
+ AstNode? parent, List<AstNode>? childNodes) {
if (parent is Block ||
parent is ClassDeclaration ||
parent is CompilationUnit ||
@@ -1546,7 +1546,7 @@
return null;
} else {
var result = <Token>[];
- for (var child in childNodes) {
+ for (var child in childNodes!) {
var separator = child.endToken.next;
if (separator != null && separator.type == TokenType.COMMA) {
result.add(separator);
@@ -1562,8 +1562,8 @@
/// [EditPlan] representing an AstNode that is not to be changed, but may have
/// some changes applied to some of its descendants.
class _PassThroughEditPlan extends _SimpleEditPlan {
- _PassThroughEditPlan._(AstNode node, Precedence precedence,
- bool endsInCascade, Map<int, List<AtomicEdit>> innerChanges)
+ _PassThroughEditPlan._(AstNode? node, Precedence precedence,
+ bool endsInCascade, Map<int?, List<AtomicEdit>>? innerChanges)
: super(node, precedence, endsInCascade, innerChanges);
}
@@ -1585,12 +1585,12 @@
: super(node, innerPlan);
@override
- Map<int, List<AtomicEdit>> _getChanges(bool parens) {
+ Map<int?, List<AtomicEdit>>? _getChanges(bool parens) {
var changes = innerPlan._getChanges(false);
if (!parens) {
changes ??= {};
(changes[offset] ??= []).insert(0, const AtomicEdit.delete(1));
- (changes[end - 1] ??= []).add(const AtomicEdit.delete(1));
+ (changes[end! - 1] ??= []).add(const AtomicEdit.delete(1));
}
return changes;
}
@@ -1623,7 +1623,7 @@
/// contiguous.
class _RemoveEditPlan extends EditPlan {
@override
- final AstNode parentNode;
+ final AstNode? parentNode;
/// Index of the node to be removed within the parent.
final int firstChildIndex;
@@ -1631,7 +1631,7 @@
/// Index of the node to be removed within the parent.
final int lastChildIndex;
- final AtomicEditInfo info;
+ final AtomicEditInfo? info;
_RemoveEditPlan(
this.parentNode, this.firstChildIndex, this.lastChildIndex, this.info)
@@ -1646,16 +1646,16 @@
@override
final bool endsInCascade;
- final Map<int, List<AtomicEdit>> _innerChanges;
+ final Map<int?, List<AtomicEdit>>? _innerChanges;
bool _finalized = false;
_SimpleEditPlan(
- AstNode node, this._precedence, this.endsInCascade, this._innerChanges)
+ AstNode? node, this._precedence, this.endsInCascade, this._innerChanges)
: super._(node);
@override
- Map<int, List<AtomicEdit>> _getChanges(bool parens) {
+ Map<int?, List<AtomicEdit>>? _getChanges(bool parens) {
assert(!_finalized);
_finalized = true;
return parens ? _createAddParenChanges(_innerChanges) : _innerChanges;
@@ -1663,7 +1663,7 @@
@override
bool _parensNeeded(
- {@required Precedence threshold,
+ {required Precedence threshold,
bool associative = false,
bool allowCascade = false}) {
if (endsInCascade && !allowCascade) return true;
@@ -1683,7 +1683,7 @@
final AstNode parentNode;
/// The changes to be made.
- final Map<int, List<AtomicEdit>> changes;
+ final Map<int?, List<AtomicEdit>> changes;
_TokenChangePlan(this.parentNode, this.changes) : super._();
}
@@ -1709,9 +1709,9 @@
}
/// Extension containing useful operations on a map from offsets to lists of
-/// [AtomicEdit]s. This data structure is used by [EditPlans] to accumulate
+/// [AtomicEdit]s. This data structure is used by [EditPlan]s to accumulate
/// source file changes.
-extension AtomicEditMap on Map<int, List<AtomicEdit>> {
+extension AtomicEditMap on Map<int?, List<AtomicEdit>>? {
/// Applies the changes to source file text.
///
/// If [includeInformative] is `true`, informative edits are included;
@@ -1728,23 +1728,24 @@
/// otherwise they are ignored.
List<SourceEdit> toSourceEdits({bool includeInformative = false}) {
return [
- for (var offset in keys.toList()..sort((a, b) => b.compareTo(a)))
- this[offset]
- .toSourceEdit(offset, includeInformative: includeInformative)
+ for (var offset in this!.keys.toList()..sort((a, b) => b!.compareTo(a!)))
+ this![offset]!
+ .toSourceEdit(offset!, includeInformative: includeInformative)
];
}
/// Destructively combines two change representations. If one or the other
/// input is null, the other input is returned unchanged for efficiency.
- Map<int, List<AtomicEdit>> operator +(Map<int, List<AtomicEdit>> newChanges) {
+ Map<int?, List<AtomicEdit>>? operator +(
+ Map<int?, List<AtomicEdit>>? newChanges) {
if (newChanges == null) return this;
if (this == null) {
return newChanges;
} else {
for (var entry in newChanges.entries) {
- var currentValue = this[entry.key];
+ var currentValue = this![entry.key];
if (currentValue == null) {
- this[entry.key] = entry.value;
+ this![entry.key] = entry.value;
} else {
currentValue.addAll(entry.value);
}
diff --git a/pkg/nnbd_migration/lib/src/exceptions.dart b/pkg/nnbd_migration/lib/src/exceptions.dart
index 54ca2ba..e6493cc 100644
--- a/pkg/nnbd_migration/lib/src/exceptions.dart
+++ b/pkg/nnbd_migration/lib/src/exceptions.dart
@@ -18,7 +18,7 @@
/// incorrectly configured experiment flags/nnbd sources.
static void sanityCheck(ResolvedUnitResult result) {
final equalsParamType = result.typeProvider.objectType
- .getMethod('==')
+ .getMethod('==')!
.parameters[0]
.type
.getDisplayString(withNullability: true);
diff --git a/pkg/nnbd_migration/lib/src/expression_checks.dart b/pkg/nnbd_migration/lib/src/expression_checks.dart
index 9379c9d..4ffd65b 100644
--- a/pkg/nnbd_migration/lib/src/expression_checks.dart
+++ b/pkg/nnbd_migration/lib/src/expression_checks.dart
@@ -19,7 +19,7 @@
/// checked using an `as` expression).
class ExpressionChecks {
/// All nullability edges that are related to this potential check.
- final Map<FixReasonTarget, NullabilityEdge> edges = {};
+ final Map<FixReasonTarget?, NullabilityEdge> edges = {};
ExpressionChecks();
}
@@ -36,7 +36,7 @@
/// package.
final bool isSetupAssignment;
- ExpressionChecksOrigin(Source source, Expression node, this.checks,
+ ExpressionChecksOrigin(Source? source, Expression? node, this.checks,
{this.isSetupAssignment = false})
: super(source, node);
diff --git a/pkg/nnbd_migration/lib/src/fix_aggregator.dart b/pkg/nnbd_migration/lib/src/fix_aggregator.dart
index 2df2b83..7e74f7e 100644
--- a/pkg/nnbd_migration/lib/src/fix_aggregator.dart
+++ b/pkg/nnbd_migration/lib/src/fix_aggregator.dart
@@ -31,7 +31,7 @@
/// Creates a [NodeProducingEditPlan] that applies the change to [innerPlan].
NodeProducingEditPlan applyExpression(FixAggregator aggregator,
- NodeProducingEditPlan innerPlan, AtomicEditInfo info);
+ NodeProducingEditPlan innerPlan, AtomicEditInfo? info);
/// Creates a string that applies the change to the [inner] text string.
String applyText(FixAggregator aggregator, String inner);
@@ -45,7 +45,7 @@
class FixAggregator extends UnifyingAstVisitor<void> {
/// Map from the [AstNode]s that need to have changes made, to the changes
/// that need to be applied to them.
- final Map<AstNode, NodeChange> _changes;
+ final Map<AstNode?, NodeChange> _changes;
/// The set of [EditPlan]s being accumulated.
List<EditPlan> _plans = [];
@@ -54,9 +54,9 @@
/// Map from library to the prefix we should use when inserting code that
/// refers to it.
- final Map<LibraryElement, String> _importPrefixes = {};
+ final Map<LibraryElement?, String?> _importPrefixes = {};
- final bool _warnOnWeakCode;
+ final bool? _warnOnWeakCode;
FixAggregator._(this.planner, this._changes, this._warnOnWeakCode,
CompilationUnitElement compilationUnitElement) {
@@ -73,7 +73,7 @@
///
/// TODO(paulberry): if the element is not currently imported, we should
/// update or add an import statement as necessary.
- String elementToCode(Element element) {
+ String? elementToCode(Element element) {
var name = element.name;
var library = element.library;
var prefix = _importPrefixes[library];
@@ -106,12 +106,12 @@
/// Gathers all the changes to [node] and its descendants into a single
/// [EditPlan].
- EditPlan planForNode(AstNode node) {
+ EditPlan planForNode(AstNode? node) {
var change = _changes[node];
if (change != null) {
- return change._apply(node, this);
+ return change._apply(node!, this);
} else {
- return planner.passThrough(node, innerPlans: innerPlansForNode(node));
+ return planner.passThrough(node, innerPlans: innerPlansForNode(node!));
}
}
@@ -189,22 +189,20 @@
var change = _changes[node];
if (change != null) {
var innerPlan = change._apply(node, this);
- if (innerPlan != null) {
- _plans.add(innerPlan);
- }
+ _plans.add(innerPlan);
} else {
node.visitChildren(this);
}
}
/// Runs the [FixAggregator] on a [unit] and returns the resulting edits.
- static Map<int, List<AtomicEdit>> run(
- CompilationUnit unit, String sourceText, Map<AstNode, NodeChange> changes,
- {bool removeViaComments = false, bool warnOnWeakCode = false}) {
+ static Map<int?, List<AtomicEdit>>? run(CompilationUnit unit,
+ String? sourceText, Map<AstNode?, NodeChange> changes,
+ {bool? removeViaComments = false, bool? warnOnWeakCode = false}) {
var planner = EditPlanner(unit.lineInfo, sourceText,
removeViaComments: removeViaComments);
- var aggregator =
- FixAggregator._(planner, changes, warnOnWeakCode, unit.declaredElement);
+ var aggregator = FixAggregator._(
+ planner, changes, warnOnWeakCode, unit.declaredElement!);
unit.accept(aggregator);
if (aggregator._plans.isEmpty) return {};
EditPlan plan;
@@ -225,7 +223,7 @@
/// Indicates whether this is a downcast.
final bool isDowncast;
- IntroduceAsChange(this.type, {@required this.isDowncast}) : super(type);
+ IntroduceAsChange(this.type, {required this.isDowncast}) : super(type);
@override
NullabilityFixDescription get description => isDowncast
@@ -234,7 +232,7 @@
@override
NodeProducingEditPlan applyExpression(FixAggregator aggregator,
- NodeProducingEditPlan innerPlan, AtomicEditInfo info) =>
+ NodeProducingEditPlan innerPlan, AtomicEditInfo? info) =>
aggregator.planner.addBinaryPostfix(
innerPlan, TokenType.AS, aggregator.typeToCode(type),
info: info);
@@ -262,7 +260,7 @@
@override
NodeProducingEditPlan applyExpression(FixAggregator aggregator,
- NodeProducingEditPlan innerPlan, AtomicEditInfo info) =>
+ NodeProducingEditPlan innerPlan, AtomicEditInfo? info) =>
aggregator.planner.addPostfix(innerPlan,
'.then((value) => ${innerChange.applyText(aggregator, 'value')})',
info: info);
@@ -315,7 +313,7 @@
/// Creates the appropriate specialized kind of [NodeChange] appropriate for
/// the given [node].
- static NodeChange<AstNode> create(AstNode node) =>
+ static NodeChange<AstNode>? create(AstNode node) =>
node.accept(_NodeChangeVisitor._instance);
}
@@ -327,7 +325,7 @@
/// If [changeToRequiredKeyword] is `true`, the information that should be
/// contained in the edit.
- AtomicEditInfo changeToRequiredKeywordInfo;
+ AtomicEditInfo? changeToRequiredKeywordInfo;
NodeChangeForAnnotation() : super._();
@@ -342,11 +340,10 @@
}
var name = node.name;
if (name is PrefixedIdentifier) {
- name = (name as PrefixedIdentifier).identifier;
+ name = name.identifier;
}
- if (name != null &&
- aggregator.planner.sourceText.substring(name.offset, name.end) ==
- 'required') {
+ if (aggregator.planner.sourceText!.substring(name.offset, name.end) ==
+ 'required') {
// The text `required` already exists in the annotation; we can just
// extract it.
return aggregator.planner.extract(
@@ -364,7 +361,7 @@
class NodeChangeForArgumentList extends NodeChange<ArgumentList> {
/// Map whose keys are the arguments that should be dropped from this argument
/// list, if any. Values are info about why the arguments are being dropped.
- final Map<Expression, AtomicEditInfo> _argumentsToDrop = {};
+ final Map<Expression, AtomicEditInfo?> _argumentsToDrop = {};
NodeChangeForArgumentList() : super._();
@@ -372,7 +369,7 @@
/// this argument list, if any. Values are info about why the arguments are
/// being dropped.
@visibleForTesting
- Map<Expression, AtomicEditInfo> get argumentsToDrop => _argumentsToDrop;
+ Map<Expression, AtomicEditInfo?> get argumentsToDrop => _argumentsToDrop;
@override
Iterable<String> get _toStringParts =>
@@ -380,7 +377,7 @@
/// Updates `this` so that the given [argument] will be dropped, using [info]
/// to annotate the reason why it is being dropped.
- void dropArgument(Expression argument, AtomicEditInfo info) {
+ void dropArgument(Expression argument, AtomicEditInfo? info) {
assert(!_argumentsToDrop.containsKey(argument));
_argumentsToDrop[argument] = info;
}
@@ -442,7 +439,7 @@
NodeProducingEditPlan _apply(
AssignmentExpression node, FixAggregator aggregator) {
var lhsPlan = aggregator.planForNode(node.leftHandSide);
- if (isWeakNullAware && !aggregator._warnOnWeakCode) {
+ if (isWeakNullAware && !aggregator._warnOnWeakCode!) {
// Just keep the LHS
return aggregator.planner.extract(node, lhsPlan as NodeProducingEditPlan,
infoAfter: AtomicEditInfo(
@@ -459,12 +456,12 @@
aggregator.planner.passThrough(node, innerPlans: innerPlans));
}
- EditPlan _makeOperatorPlan(
+ EditPlan? _makeOperatorPlan(
FixAggregator aggregator, AssignmentExpression node, Token operator) {
var operatorPlan = super._makeOperatorPlan(aggregator, node, operator);
if (operatorPlan != null) return operatorPlan;
if (isWeakNullAware) {
- assert(aggregator._warnOnWeakCode);
+ assert(aggregator._warnOnWeakCode!);
return aggregator.planner.informativeMessageForToken(node, operator,
info: AtomicEditInfo(
NullabilityFixDescription
@@ -496,7 +493,8 @@
if (hasNullableSource) 'hasNullableSource'
];
- EditPlan _makeOperatorPlan(FixAggregator aggregator, N node, Token operator) {
+ EditPlan? _makeOperatorPlan(
+ FixAggregator aggregator, N node, Token operator) {
if (hasNullableSource) {
return aggregator.planner.informativeMessageForToken(node, operator,
info: AtomicEditInfo(
@@ -550,8 +548,7 @@
EditPlan _apply(CompilationUnit node, FixAggregator aggregator) {
List<EditPlan> innerPlans = [];
if (removeLanguageVersionComment) {
- final comment = (node as CompilationUnitImpl).languageVersionToken;
- assert(comment != null);
+ final comment = (node as CompilationUnitImpl).languageVersionToken!;
innerPlans.add(aggregator.planner.replaceToken(node, comment, '',
info: AtomicEditInfo(
NullabilityFixDescription.removeLanguageVersionComment,
@@ -588,7 +585,7 @@
if (node.directives.every((d) => d is LibraryDirective)) {
while (importsToAdd.isNotEmpty) {
insertImport(
- node.declarations.beginToken.offset, importsToAdd.removeAt(0),
+ node.declarations.beginToken!.offset, importsToAdd.removeAt(0),
suffix: importsToAdd.isEmpty ? '\n\n' : '\n');
}
} else {
@@ -610,7 +607,7 @@
/// an existing [directive].
bool _shouldImportGoBefore(String newImportUri, Directive directive) {
if (directive is ImportDirective) {
- return newImportUri.compareTo(directive.uriContent) < 0;
+ return newImportUri.compareTo(directive.uriContent!) < 0;
} else if (directive is LibraryDirective) {
// Library directives must come before imports.
return false;
@@ -628,11 +625,11 @@
/// If not `null`, indicates that the condition expression is known to
/// evaluate to either `true` or `false`, so the other branch of the
/// conditional is dead code and should be eliminated.
- bool conditionValue;
+ bool? conditionValue;
/// If [conditionValue] is not `null`, the reason that should be included in
/// the [AtomicEditInfo] for the edit that removes the dead code.
- FixReasonInfo conditionReason;
+ FixReasonInfo? conditionReason;
@override
Iterable<String> get _toStringParts =>
@@ -641,13 +638,13 @@
/// If dead code removal is warranted for [node], returns an [EditPlan] that
/// removes the dead code (and performs appropriate updates within any
/// descendant AST nodes that remain). Otherwise returns `null`.
- EditPlan _applyConditional(N node, FixAggregator aggregator,
- AstNode conditionNode, AstNode thenNode, AstNode elseNode) {
+ EditPlan? _applyConditional(N node, FixAggregator aggregator,
+ AstNode conditionNode, AstNode thenNode, AstNode? elseNode) {
if (conditionValue == null) return null;
- if (aggregator._warnOnWeakCode) {
+ if (aggregator._warnOnWeakCode!) {
var conditionPlan = aggregator.innerPlanForNode(conditionNode);
var info = AtomicEditInfo(
- conditionValue
+ conditionValue!
? NullabilityFixDescription.conditionTrueInStrongMode
: NullabilityFixDescription.conditionFalseInStrongMode,
{FixReasonTarget.root: conditionReason});
@@ -660,9 +657,9 @@
if (elseNode != null) aggregator.planForNode(elseNode)
]);
}
- AstNode nodeToKeep;
+ AstNode? nodeToKeep;
NullabilityFixDescription descriptionBefore, descriptionAfter;
- if (conditionValue) {
+ if (conditionValue!) {
nodeToKeep = thenNode;
descriptionBefore = NullabilityFixDescription.discardCondition;
if (elseNode == null) {
@@ -708,7 +705,7 @@
var infoAfter = AtomicEditInfo(
descriptionAfter, {FixReasonTarget.root: conditionReason});
if (nodeToKeep is Block && nodeToKeep.statements.length == 1) {
- var singleStatement = (nodeToKeep as Block).statements[0];
+ var singleStatement = nodeToKeep.statements[0];
if (singleStatement is VariableDeclarationStatement) {
// It's not safe to eliminate the {} because it increases the scope of
// the variable declarations
@@ -744,19 +741,19 @@
/// If [addRequiredKeyword] is `true`, the information that should be
/// contained in the edit.
- AtomicEditInfo addRequiredKeywordInfo;
+ AtomicEditInfo? addRequiredKeywordInfo;
/// If [addRequiredKeyword] is `true`, and there is a `/*required*/` hint,
/// the hint comment that should be converted into a simple `required` string.
- HintComment requiredHint;
+ HintComment? requiredHint;
/// If non-null, indicates a `@required` annotation which should be removed
/// from this node.
- Annotation annotationToRemove;
+ Annotation? annotationToRemove;
/// If [annotationToRemove] is non-null, the information that should be
/// contained in the edit.
- AtomicEditInfo removeAnnotationInfo;
+ AtomicEditInfo? removeAnnotationInfo;
NodeChangeForDefaultFormalParameter() : super._();
@@ -770,18 +767,18 @@
if (requiredHint != null) {
var innerPlan = aggregator.innerPlanForNode(node);
- return aggregator.planner.acceptPrefixHint(innerPlan, requiredHint,
+ return aggregator.planner.acceptPrefixHint(innerPlan, requiredHint!,
info: addRequiredKeywordInfo);
}
- var offset = node.firstTokenAfterCommentAndMetadata.offset;
+ var offset = node.firstTokenAfterCommentAndMetadata!.offset;
return aggregator.planner.passThrough(node, innerPlans: [
aggregator.planner.insertText(node, offset, [
AtomicEdit.insert('required ', info: addRequiredKeywordInfo),
]),
if (annotationToRemove != null)
aggregator.planner
- .removeNode(annotationToRemove, info: removeAnnotationInfo),
+ .removeNode(annotationToRemove!, info: removeAnnotationInfo),
...aggregator.innerPlansForNode(node),
]);
}
@@ -796,7 +793,7 @@
/// The list of [AtomicEditInfo] objects corresponding to each change in
/// [expressionChanges].
- final List<AtomicEditInfo> expressionChangeInfos = [];
+ final List<AtomicEditInfo?> expressionChangeInfos = [];
NodeChangeForExpression() : super._();
@@ -806,7 +803,7 @@
expressionChange.describe()
];
- void addExpressionChange(ExpressionChange change, AtomicEditInfo info) {
+ void addExpressionChange(ExpressionChange change, AtomicEditInfo? info) {
expressionChanges.add(change);
expressionChangeInfos.add(info);
}
@@ -837,7 +834,7 @@
extends NodeChangeForType<FieldFormalParameter> {
/// If not `null`, an explicit type annotation that should be added to the
/// parameter.
- DartType addExplicitType;
+ DartType? addExplicitType;
NodeChangeForFieldFormalParameter() : super._();
@@ -848,7 +845,7 @@
@override
EditPlan _apply(FieldFormalParameter node, FixAggregator aggregator) {
if (addExplicitType != null) {
- var typeText = aggregator.typeToCode(addExplicitType);
+ var typeText = aggregator.typeToCode(addExplicitType!);
// Even a field formal parameter can use `var`, `final`.
if (node.keyword?.keyword == Keyword.VAR) {
// TODO(srawlins): Test instrumentation info.
@@ -856,7 +853,7 @@
AtomicEditInfo(NullabilityFixDescription.replaceVar(typeText), {});
return aggregator.planner.passThrough(node, innerPlans: [
aggregator.planner
- .replaceToken(node, node.keyword, typeText, info: info),
+ .replaceToken(node, node.keyword!, typeText, info: info),
...aggregator.innerPlansForNode(node),
]);
} else {
@@ -932,9 +929,9 @@
var innerPlans = [
if (targetPlan != null) targetPlan,
if (nullAwarePlan != null) nullAwarePlan,
- if (methodNamePlan != null) methodNamePlan,
+ methodNamePlan,
if (typeArgumentsPlan != null) typeArgumentsPlan,
- if (argumentListPlan != null) argumentListPlan
+ argumentListPlan
];
return _applyExpression(aggregator,
aggregator.planner.passThrough(node, innerPlans: innerPlans));
@@ -946,21 +943,21 @@
class NodeChangeForMethodName extends NodeChange<SimpleIdentifier> {
/// The name the method name should be changed to, or `null` if no change
/// should be made.
- String _replacement;
+ String? _replacement;
/// Info object associated with the replacement.
- AtomicEditInfo _replacementInfo;
+ AtomicEditInfo? _replacementInfo;
NodeChangeForMethodName() : super._();
/// Queries the name the method name should be changed to, or `null` if no
/// change should be made.
@visibleForTesting
- String get replacement => _replacement;
+ String? get replacement => _replacement;
/// Queries the info object associated with the replacement.
@visibleForTesting
- AtomicEditInfo get replacementInfo => _replacementInfo;
+ AtomicEditInfo? get replacementInfo => _replacementInfo;
@override
Iterable<String> get _toStringParts =>
@@ -968,7 +965,7 @@
/// Updates `this` so that the method name will be changed to [replacement],
/// using [info] to annotate the reason for the change.
- void replaceWith(String replacement, AtomicEditInfo info) {
+ void replaceWith(String replacement, AtomicEditInfo? info) {
assert(_replacement == null);
_replacement = replacement;
_replacementInfo = info;
@@ -978,7 +975,7 @@
EditPlan _apply(SimpleIdentifier node, FixAggregator aggregator) {
if (replacement != null) {
return aggregator.planner.replace(
- node, [AtomicEdit.insert(replacement, info: replacementInfo)],
+ node, [AtomicEdit.insert(replacement!, info: replacementInfo)],
info: replacementInfo);
} else {
return aggregator.innerPlanForNode(node);
@@ -998,14 +995,14 @@
/// Returns an [EditPlan] that removes null awareness, if appropriate.
/// Otherwise returns `null`.
- EditPlan _applyNullAware(N node, FixAggregator aggregator) {
+ EditPlan? _applyNullAware(N node, FixAggregator aggregator) {
if (!removeNullAwareness) return null;
- var description = aggregator._warnOnWeakCode
+ var description = aggregator._warnOnWeakCode!
? NullabilityFixDescription.nullAwarenessUnnecessaryInStrongMode
: NullabilityFixDescription.removeNullAwareness;
return aggregator.planner.removeNullAwareness(node,
info: AtomicEditInfo(description, const {}),
- isInformative: aggregator._warnOnWeakCode);
+ isInformative: aggregator._warnOnWeakCode!);
}
}
@@ -1061,7 +1058,7 @@
var innerPlans = [
if (targetPlan != null) targetPlan,
if (nullAwarePlan != null) nullAwarePlan,
- if (propertyNamePlan != null) propertyNamePlan
+ propertyNamePlan
];
return _applyExpression(aggregator,
aggregator.planner.passThrough(node, innerPlans: innerPlans));
@@ -1128,7 +1125,7 @@
extends NodeChange<SimpleFormalParameter> {
/// If not `null`, an explicit type annotation that should be added to the
/// parameter.
- DartType addExplicitType;
+ DartType? addExplicitType;
NodeChangeForSimpleFormalParameter() : super._();
@@ -1140,14 +1137,14 @@
EditPlan _apply(SimpleFormalParameter node, FixAggregator aggregator) {
var innerPlan = aggregator.innerPlanForNode(node);
if (addExplicitType == null) return innerPlan;
- var typeText = aggregator.typeToCode(addExplicitType);
+ var typeText = aggregator.typeToCode(addExplicitType!);
if (node.keyword?.keyword == Keyword.VAR) {
// TODO(srawlins): Test instrumentation info.
var info =
AtomicEditInfo(NullabilityFixDescription.replaceVar(typeText), {});
return aggregator.planner.passThrough(node, innerPlans: [
aggregator.planner
- .replaceToken(node, node.keyword, typeText, info: info),
+ .replaceToken(node, node.keyword!, typeText, info: info),
...aggregator.innerPlansForNode(node),
]);
} else {
@@ -1156,7 +1153,7 @@
AtomicEditInfo(NullabilityFixDescription.addType(typeText), {});
// Skip past the offset of any metadata, a potential `final` keyword, and
// a potential `covariant` keyword.
- var offset = node.type?.offset ?? node.identifier.offset;
+ var offset = node.type?.offset ?? node.identifier!.offset;
return aggregator.planner.passThrough(node, innerPlans: [
aggregator.planner.insertText(node, offset,
[AtomicEdit.insert(typeText, info: info), AtomicEdit.insert(' ')]),
@@ -1171,14 +1168,14 @@
abstract class NodeChangeForType<N extends AstNode> extends NodeChange<N> {
bool _makeNullable = false;
- HintComment _nullabilityHint;
+ HintComment? _nullabilityHint;
/// The decorated type of the type annotation, or `null` if there is no
/// decorated type info of interest. If [makeNullable] is `true`, the node
/// from this type will be attached to the edit that adds the `?`. If
/// [_makeNullable] is `false`, the node from this type will be attached to
/// the information about why the node wasn't made nullable.
- DecoratedType _decoratedType;
+ DecoratedType? _decoratedType;
NodeChangeForType._() : super._();
@@ -1190,7 +1187,7 @@
/// If we are making the type nullable due to a hint, the comment that caused
/// it.
- HintComment get nullabilityHint => _nullabilityHint;
+ HintComment? get nullabilityHint => _nullabilityHint;
@override
Iterable<String> get _toStringParts => [
@@ -1199,7 +1196,7 @@
];
void recordNullability(DecoratedType decoratedType, bool makeNullable,
- {HintComment nullabilityHint}) {
+ {HintComment? nullabilityHint}) {
_decoratedType = decoratedType;
_makeNullable = makeNullable;
_nullabilityHint = nullabilityHint;
@@ -1209,8 +1206,9 @@
EditPlan _apply(N node, FixAggregator aggregator) {
var innerPlan = aggregator.innerPlanForNode(node);
if (_decoratedType == null) return innerPlan;
- var typeName = _decoratedType.type.getDisplayString(withNullability: false);
- var fixReasons = {FixReasonTarget.root: _decoratedType.node};
+ var typeName =
+ _decoratedType!.type!.getDisplayString(withNullability: false);
+ var fixReasons = {FixReasonTarget.root: _decoratedType!.node};
if (_makeNullable) {
var hint = _nullabilityHint;
if (hint != null) {
@@ -1256,15 +1254,15 @@
extends NodeChange<VariableDeclarationList> {
/// If an explicit type should be added to this variable declaration, the type
/// that should be added. Otherwise `null`.
- DartType addExplicitType;
+ DartType? addExplicitType;
/// Indicates whether a "late" annotation should be added to this variable
/// declaration, caused by inference.
- LateAdditionReason lateAdditionReason;
+ LateAdditionReason? lateAdditionReason;
/// If a "late" annotation should be added to this variable declaration, and
/// the cause is a "late" hint, the hint that caused it. Otherwise `null`.
- HintComment lateHint;
+ HintComment? lateHint;
NodeChangeForVariableDeclarationList() : super._();
@@ -1289,12 +1287,12 @@
[AtomicEdit.insert('late', info: info), AtomicEdit.insert(' ')]));
}
if (addExplicitType != null) {
- var typeText = aggregator.typeToCode(addExplicitType);
+ var typeText = aggregator.typeToCode(addExplicitType!);
if (node.keyword?.keyword == Keyword.VAR) {
var info =
AtomicEditInfo(NullabilityFixDescription.replaceVar(typeText), {});
innerPlans.add(aggregator.planner
- .replaceToken(node, node.keyword, typeText, info: info));
+ .replaceToken(node, node.keyword!, typeText, info: info));
} else {
var info =
AtomicEditInfo(NullabilityFixDescription.addType(typeText), {});
@@ -1307,10 +1305,10 @@
innerPlans.addAll(aggregator.innerPlansForNode(node));
var plan = aggregator.planner.passThrough(node, innerPlans: innerPlans);
if (lateHint != null) {
- var description = lateHint.kind == HintCommentKind.late_
+ var description = lateHint!.kind == HintCommentKind.late_
? NullabilityFixDescription.addLateDueToHint
: NullabilityFixDescription.addLateFinalDueToHint;
- plan = aggregator.planner.acceptPrefixHint(plan, lateHint,
+ plan = aggregator.planner.acceptPrefixHint(plan, lateHint!,
info: AtomicEditInfo(description, {}, hintComment: lateHint));
}
return plan;
@@ -1328,7 +1326,7 @@
@override
NodeProducingEditPlan applyExpression(FixAggregator aggregator,
- NodeProducingEditPlan innerPlan, AtomicEditInfo info) =>
+ NodeProducingEditPlan innerPlan, AtomicEditInfo? info) =>
aggregator.planner.addCommentPostfix(
innerPlan, '/* no valid migration */',
info: info, isInformative: true);
@@ -1344,7 +1342,7 @@
/// [ExpressionChange] describing the addition of an `!` after an expression.
class NullCheckChange extends ExpressionChange {
/// The hint that is causing this `!` to be added, if any.
- final HintComment hint;
+ final HintComment? hint;
NullCheckChange(DartType resultType, {this.hint}) : super(resultType);
@@ -1354,9 +1352,9 @@
@override
NodeProducingEditPlan applyExpression(FixAggregator aggregator,
- NodeProducingEditPlan innerPlan, AtomicEditInfo info) {
+ NodeProducingEditPlan innerPlan, AtomicEditInfo? info) {
if (hint != null) {
- return aggregator.planner.acceptSuffixHint(innerPlan, hint, info: info);
+ return aggregator.planner.acceptSuffixHint(innerPlan, hint!, info: info);
} else {
return aggregator.planner
.addUnaryPostfix(innerPlan, TokenType.BANG, info: info);
@@ -1453,7 +1451,7 @@
NodeChangeForSimpleFormalParameter();
@override
- NodeChange visitSimpleIdentifier(SimpleIdentifier node) {
+ NodeChange? visitSimpleIdentifier(SimpleIdentifier node) {
var parent = node.parent;
if (parent is MethodInvocation && identical(node, parent.methodName)) {
return NodeChangeForMethodName();
diff --git a/pkg/nnbd_migration/lib/src/fix_builder.dart b/pkg/nnbd_migration/lib/src/fix_builder.dart
index 6a7731b..77fc0d3 100644
--- a/pkg/nnbd_migration/lib/src/fix_builder.dart
+++ b/pkg/nnbd_migration/lib/src/fix_builder.dart
@@ -25,6 +25,7 @@
import 'package:analyzer/src/generated/resolver.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/utilities_dart.dart';
+import 'package:collection/collection.dart' show IterableExtension;
import 'package:meta/meta.dart';
import 'package:nnbd_migration/fix_reason_target.dart';
import 'package:nnbd_migration/instrumentation.dart';
@@ -84,7 +85,7 @@
/// actually make the changes; it simply reports what changes are necessary
/// through abstract methods.
class FixBuilder {
- final DecoratedClassHierarchy _decoratedClassHierarchy;
+ final DecoratedClassHierarchy? _decoratedClassHierarchy;
/// The type provider providing non-nullable types.
final TypeProvider typeProvider;
@@ -97,18 +98,18 @@
final TypeSystemImpl _typeSystem;
/// Variables for this migration run.
- final Variables _variables;
+ final Variables? _variables;
/// The file being analyzed.
- final Source source;
+ final Source? source;
- ResolverVisitor _resolver;
+ late ResolverVisitor _resolver;
/// The listener to which exceptions should be reported.
- final NullabilityMigrationListener listener;
+ final NullabilityMigrationListener? listener;
/// The compilation unit for which fixes are being built.
- final CompilationUnit unit;
+ final CompilationUnit? unit;
final MigrationResolutionHooksImpl migrationResolutionHooks;
@@ -116,7 +117,7 @@
/// that type should be.
final Map<ParameterElement, DartType> _addedParameterTypes = {};
- final bool warnOnWeakCode;
+ final bool? warnOnWeakCode;
final NullabilityGraph _graph;
@@ -135,15 +136,15 @@
final Map<String, Version> _neededPackages;
factory FixBuilder(
- Source source,
- DecoratedClassHierarchy decoratedClassHierarchy,
+ Source? source,
+ DecoratedClassHierarchy? decoratedClassHierarchy,
TypeProvider typeProvider,
TypeSystemImpl typeSystem,
- Variables variables,
+ Variables? variables,
LibraryElement definingLibrary,
- NullabilityMigrationListener listener,
- CompilationUnit unit,
- bool warnOnWeakCode,
+ NullabilityMigrationListener? listener,
+ CompilationUnit? unit,
+ bool? warnOnWeakCode,
NullabilityGraph graph,
Map<String, Version> neededPackages) {
var migrationResolutionHooks = MigrationResolutionHooksImpl();
@@ -193,7 +194,7 @@
_resolver = ResolverVisitorForMigration(
inheritanceManager,
definingLibrary,
- source,
+ source!,
typeProvider,
errorListener,
_typeSystem,
@@ -206,12 +207,12 @@
void visitAll() {
try {
ElementTypeProvider.current = migrationResolutionHooks;
- unit.accept(_FixBuilderPreVisitor(this));
- unit.accept(_resolver);
- unit.accept(_FixBuilderPostVisitor(this));
+ unit!.accept(_FixBuilderPreVisitor(this));
+ unit!.accept(_resolver);
+ unit!.accept(_FixBuilderPostVisitor(this));
} catch (exception, stackTrace) {
if (listener != null) {
- listener.reportException(source, unit, exception, stackTrace);
+ listener!.reportException(source, unit, exception, stackTrace);
} else {
rethrow;
}
@@ -232,14 +233,14 @@
/// type of the class within which [element] is being accessed; this is used
/// to perform the correct substitutions.
DartType _computeMigratedType(Element element) {
- element = element.declaration;
+ element = element.declaration!;
if (element is ClassElement || element is TypeParameterElement) {
return typeProvider.typeType;
} else if (element is PropertyAccessorElement &&
element.isSynthetic &&
!element.variable.isSynthetic) {
- var variableType = _variables
- .toFinalType(_variables.decoratedElementType(element.variable));
+ var variableType = _variables!
+ .toFinalType(_variables!.decoratedElementType(element.variable));
if (element.isSetter) {
return FunctionTypeImpl(
returnType: typeProvider.voidType,
@@ -257,20 +258,20 @@
nullabilitySuffix: NullabilitySuffix.none);
}
} else {
- return _variables.toFinalType(_variables.decoratedElementType(element));
+ return _variables!.toFinalType(_variables!.decoratedElementType(element));
}
}
/// Returns the [NodeChange] object accumulating changes for the given [node],
/// creating it if necessary.
NodeChange _getChange(AstNode node) =>
- changes[node] ??= NodeChange.create(node);
+ changes[node] ??= NodeChange.create(node)!;
/// Determines whether the given [node], which is a null-aware method
/// invocation, property access, or index expression, should remain null-aware
/// after migration.
bool _shouldStayNullAware(Expression node) {
- Expression target;
+ Expression? target;
if (node is PropertyAccess) {
target = node.target;
} else if (node is MethodInvocation) {
@@ -278,7 +279,7 @@
} else {
throw StateError('Unexpected expression type: ${node.runtimeType}');
}
- if (!_typeSystem.isPotentiallyNullable(target.staticType)) {
+ if (!_typeSystem.isPotentiallyNullable(target!.staticType!)) {
(_getChange(node) as NodeChangeForNullAware).removeNullAwareness = true;
return false;
}
@@ -315,7 +316,7 @@
class MigrationResolutionHooksImpl
with ResolutionUtils
implements MigrationResolutionHooks {
- FixBuilder _fixBuilder;
+ FixBuilder? _fixBuilder;
final Expando<List<CollectionElement>> _collectionElements = Expando();
@@ -324,7 +325,7 @@
final Map<Expression, _AssignmentLikeExpressionHandler>
_assignmentLikeExpressionHandlers = {};
- FlowAnalysis<AstNode, Statement, Expression, PromotableElement, DartType>
+ FlowAnalysis<AstNode, Statement, Expression, PromotableElement, DartType>?
_flowAnalysis;
/// Deferred processing that should be performed once we have finished
@@ -332,19 +333,19 @@
final Map<MethodInvocation, DartType Function(DartType)>
_deferredMethodInvocationProcessing = {};
- TypeProvider get typeProvider => _fixBuilder.typeProvider;
+ TypeProvider get typeProvider => _fixBuilder!.typeProvider;
@override
void freshTypeParameterCreated(TypeParameterElement newTypeParameter,
TypeParameterElement oldTypeParameter) {
- DecoratedTypeParameterBounds.current.put(newTypeParameter,
- DecoratedTypeParameterBounds.current.get(oldTypeParameter));
+ DecoratedTypeParameterBounds.current!.put(newTypeParameter,
+ DecoratedTypeParameterBounds.current!.get(oldTypeParameter));
}
@override
List<InterfaceType> getClassInterfaces(ClassElementImpl element) {
return _wrapExceptions(
- _fixBuilder.unit,
+ _fixBuilder!.unit,
() => element.interfacesInternal,
() => [
for (var interface in element.interfacesInternal)
@@ -353,11 +354,11 @@
}
@override
- bool getConditionalKnownValue(AstNode node) =>
+ bool? getConditionalKnownValue(AstNode node) =>
_wrapExceptions(node, () => null, () {
// TODO(paulberry): handle conditional expressions.
- var conditionalDiscard = _fixBuilder._variables
- .getConditionalDiscard(_fixBuilder.source, node);
+ var conditionalDiscard = _fixBuilder!._variables!
+ .getConditionalDiscard(_fixBuilder!.source, node);
if (conditionalDiscard == null) {
return null;
} else {
@@ -365,19 +366,19 @@
return null;
}
var conditionValue = conditionalDiscard.keepTrue;
- (_fixBuilder._getChange(node) as NodeChangeForConditional)
+ (_fixBuilder!._getChange(node) as NodeChangeForConditional)
..conditionValue = conditionValue
..conditionReason = conditionalDiscard.reason;
// If we're just issuing warnings, instruct the resolver to go ahead
// and visit both branches of the conditional.
- return _fixBuilder.warnOnWeakCode ? null : conditionValue;
+ return _fixBuilder!.warnOnWeakCode! ? null : conditionValue;
}
});
@override
List<ParameterElement> getExecutableParameters(
ExecutableElementImpl element) {
- if (_fixBuilder._graph.isBeingMigrated(element.library.source)) {
+ if (_fixBuilder!._graph.isBeingMigrated(element.library.source)) {
// The element is part of a library that's being migrated, so its
// parameters all have been visited (and thus have their own final
// types). So we don't need to do anything.
@@ -396,8 +397,8 @@
@override
FunctionType getExecutableType(ElementImplWithFunctionType element) =>
- _wrapExceptions(_fixBuilder.unit, () => element.typeInternal, () {
- var type = _fixBuilder._computeMigratedType(element);
+ _wrapExceptions(_fixBuilder!.unit, () => element.typeInternal, () {
+ var type = _fixBuilder!._computeMigratedType(element);
Element baseElement = element;
if (baseElement is Member) {
type = baseElement.substitution.substituteType(type);
@@ -408,17 +409,17 @@
@override
DartType getExtendedType(ExtensionElementImpl element) {
return _wrapExceptions(
- _fixBuilder.unit,
+ _fixBuilder!.unit,
() => element.extendedTypeInternal,
- () => _fixBuilder._variables
- .toFinalType(_fixBuilder._variables.decoratedElementType(element)));
+ () => _fixBuilder!._variables!.toFinalType(
+ _fixBuilder!._variables!.decoratedElementType(element)));
}
@override
DartType getFieldType(PropertyInducingElementImpl element) =>
- _wrapExceptions(_fixBuilder.unit, () => element.typeInternal, () {
+ _wrapExceptions(_fixBuilder!.unit, () => element.typeInternal, () {
assert(!element.isSynthetic);
- return _fixBuilder._computeMigratedType(element);
+ return _fixBuilder!._computeMigratedType(element);
});
@override
@@ -437,11 +438,11 @@
_transformCollectionElements(node.elements));
@override
- DartType getTypeParameterBound(TypeParameterElementImpl element) {
- var decoratedBound = _fixBuilder._variables
+ DartType? getTypeParameterBound(TypeParameterElementImpl element) {
+ var decoratedBound = _fixBuilder!._variables!
.decoratedTypeParameterBound(element, allowNullUnparentedBounds: true);
if (decoratedBound == null) return element.boundInternal;
- var bound = _fixBuilder._variables.toFinalType(decoratedBound);
+ var bound = _fixBuilder!._variables!.toFinalType(decoratedBound);
if (bound.isDynamic) {
return null;
} else if (bound.isDartCoreObject &&
@@ -454,7 +455,7 @@
@override
DartType getVariableType(VariableElementImpl variable) =>
- _wrapExceptions(_fixBuilder.unit, () => variable.typeInternal, () {
+ _wrapExceptions(_fixBuilder!.unit, () => variable.typeInternal, () {
if (variable.library == null) {
// This is a synthetic variable created during resolution (e.g. a
// parameter of a function type), so the type it currently has is the
@@ -467,10 +468,10 @@
enclosingElement.isSynthetic) {
// This is the parameter of a synthetic getter, so it has the same
// type as the corresponding variable.
- return _fixBuilder._computeMigratedType(enclosingElement.variable);
+ return _fixBuilder!._computeMigratedType(enclosingElement.variable);
}
}
- return _fixBuilder._computeMigratedType(variable);
+ return _fixBuilder!._computeMigratedType(variable);
});
@override
@@ -482,24 +483,25 @@
@override
bool isLibraryNonNullableByDefault(LibraryElementImpl element) {
- return _fixBuilder._graph.isBeingMigrated(element.source) ||
+ return _fixBuilder!._graph.isBeingMigrated(element.source) ||
element.isNonNullableByDefaultInternal;
}
@override
bool isMethodInvocationNullAware(MethodInvocation node) {
return node.isNullAware &&
- (_shouldStayNullAware[node] ??= _fixBuilder._shouldStayNullAware(node));
+ (_shouldStayNullAware[node] ??=
+ _fixBuilder!._shouldStayNullAware(node));
}
/// Indicates whether the given [element] is a member of an extension on a
/// potentially nullable type (and hence the extension member can be invoked
/// on a nullable type without introducing a null check).
- bool isNullableExtensionMember(Element element) {
+ bool isNullableExtensionMember(Element? element) {
if (element != null) {
var enclosingElement = element.enclosingElement;
if (enclosingElement is ExtensionElement) {
- return _fixBuilder._typeSystem
+ return _fixBuilder!._typeSystem
.isPotentiallyNullable(enclosingElement.extendedType);
}
}
@@ -509,7 +511,8 @@
@override
bool isPropertyAccessNullAware(PropertyAccess node) {
return node.isNullAware &&
- (_shouldStayNullAware[node] ??= _fixBuilder._shouldStayNullAware(node));
+ (_shouldStayNullAware[node] ??=
+ _fixBuilder!._shouldStayNullAware(node));
}
@override
@@ -526,7 +529,7 @@
if (parent.leftHandSide == node) {
return type;
}
- return _assignmentLikeExpressionHandlers[parent]
+ return _assignmentLikeExpressionHandlers[parent]!
.modifyAssignmentRhs(this, node, type);
} else if (parent is PrefixExpression) {
if (_isIncrementOrDecrementOperator(parent.operator.type)) {
@@ -546,7 +549,7 @@
var postMigrationType = parameter.type;
if (postMigrationType != type) {
// TODO(paulberry): test field formal parameters.
- _fixBuilder._addedParameterTypes[parameter] = postMigrationType;
+ _fixBuilder!._addedParameterTypes[parameter] = postMigrationType;
return postMigrationType;
}
return type;
@@ -554,23 +557,23 @@
@override
void setCompoundAssignmentExpressionTypes(CompoundAssignmentExpression node) {
- assert(_assignmentLikeExpressionHandlers[node] == null);
+ assert(_assignmentLikeExpressionHandlers[node as Expression] == null);
if (node is AssignmentExpression) {
var handler = _AssignmentExpressionHandler(node);
_assignmentLikeExpressionHandlers[node] = handler;
- handler.handleLValueType(this, node.readType, node.writeType);
+ handler.handleLValueType(this, node.readType, node.writeType!);
} else if (node is PrefixExpression) {
assert(_isIncrementOrDecrementOperator(node.operator.type));
var handler = _PrefixExpressionHandler(node);
_assignmentLikeExpressionHandlers[node] = handler;
- handler.handleLValueType(this, node.readType, node.writeType);
- handler.handleAssignmentRhs(this, _fixBuilder.typeProvider.intType);
+ handler.handleLValueType(this, node.readType, node.writeType!);
+ handler.handleAssignmentRhs(this, _fixBuilder!.typeProvider.intType);
} else if (node is PostfixExpression) {
assert(_isIncrementOrDecrementOperator(node.operator.type));
var handler = _PostfixExpressionHandler(node);
_assignmentLikeExpressionHandlers[node] = handler;
- handler.handleLValueType(this, node.readType, node.writeType);
- handler.handleAssignmentRhs(this, _fixBuilder.typeProvider.intType);
+ handler.handleLValueType(this, node.readType, node.writeType!);
+ handler.handleAssignmentRhs(this, _fixBuilder!.typeProvider.intType);
} else {
throw StateError('(${node.runtimeType}) $node');
}
@@ -578,7 +581,7 @@
@override
void setFlowAnalysis(
- FlowAnalysis<AstNode, Statement, Expression, PromotableElement, DartType>
+ FlowAnalysis<AstNode, Statement, Expression, PromotableElement, DartType>?
flowAnalysis) {
_flowAnalysis = flowAnalysis;
}
@@ -586,22 +589,22 @@
DartType _addCastOrNullCheck(
Expression node, DartType expressionType, DartType contextType) {
var checks =
- _fixBuilder._variables.expressionChecks(_fixBuilder.source, node);
+ _fixBuilder!._variables!.expressionChecks(_fixBuilder!.source, node);
var change = _createExpressionChange(node, expressionType, contextType);
var info = AtomicEditInfo(change.description, checks?.edges ?? {});
- (_fixBuilder._getChange(node) as NodeChangeForExpression)
+ (_fixBuilder!._getChange(node) as NodeChangeForExpression)
.addExpressionChange(change, info);
return change.resultType;
}
DartType _addNullCheck(Expression node, DartType type,
- {AtomicEditInfo info, HintComment hint}) {
+ {AtomicEditInfo? info, HintComment? hint}) {
var change = _createNullCheckChange(node, type, hint: hint);
var checks =
- _fixBuilder._variables.expressionChecks(_fixBuilder.source, node);
+ _fixBuilder!._variables!.expressionChecks(_fixBuilder!.source, node);
info ??= AtomicEditInfo(change.description, checks?.edges ?? {});
var nodeChangeForExpression =
- _fixBuilder._getChange(node) as NodeChangeForExpression;
+ _fixBuilder!._getChange(node) as NodeChangeForExpression;
nodeChangeForExpression.addExpressionChange(change, info);
return change.resultType;
}
@@ -619,23 +622,22 @@
}
// Either a cast or a null check is needed. We prefer to do a null
// check if we can.
- var nonNullType = _fixBuilder._typeSystem.promoteToNonNull(expressionType);
- if (_fixBuilder._typeSystem.isSubtypeOf(nonNullType, contextType)) {
+ var nonNullType = _fixBuilder!._typeSystem.promoteToNonNull(expressionType);
+ if (_fixBuilder!._typeSystem.isSubtypeOf(nonNullType, contextType)) {
return _createNullCheckChange(node, expressionType);
} else {
- if (node != null) {
- _flowAnalysis.asExpression_end(node, contextType);
- }
+ _flowAnalysis!.asExpression_end(node, contextType);
return IntroduceAsChange(contextType,
- isDowncast:
- _fixBuilder._typeSystem.isSubtypeOf(contextType, expressionType));
+ isDowncast: _fixBuilder!._typeSystem
+ .isSubtypeOf(contextType, expressionType));
}
}
ExpressionChange _createNullCheckChange(Expression node, DartType type,
- {HintComment hint}) {
- var resultType = _fixBuilder._typeSystem.promoteToNonNull(type as TypeImpl);
- _flowAnalysis.nonNullAssert_end(node);
+ {HintComment? hint}) {
+ var resultType =
+ _fixBuilder!._typeSystem.promoteToNonNull(type as TypeImpl);
+ _flowAnalysis!.nonNullAssert_end(node);
return node is NullLiteral && hint == null
? NoValidMigrationChange(resultType)
: NullCheckChange(resultType, hint: hint);
@@ -656,13 +658,13 @@
InterfaceType _getClassInterface(
ClassElement class_, ClassElement superclass) {
- var decoratedSupertype = _fixBuilder._decoratedClassHierarchy
+ var decoratedSupertype = _fixBuilder!._decoratedClassHierarchy!
.getDecoratedSupertype(class_, superclass);
- var finalType = _fixBuilder._variables.toFinalType(decoratedSupertype);
+ var finalType = _fixBuilder!._variables!.toFinalType(decoratedSupertype);
return finalType as InterfaceType;
}
- DartType _getFutureTypeArgument(DartType type) {
+ DartType? _getFutureTypeArgument(DartType type) {
if (type is InterfaceType && type.isDartAsyncFuture) {
var typeArguments = type.typeArguments;
if (typeArguments.isNotEmpty) {
@@ -673,7 +675,7 @@
}
DartType _modifyRValueType(Expression node, DartType type,
- {DartType context}) {
+ {DartType? context}) {
if (node is MethodInvocation) {
var deferredProcessing = _deferredMethodInvocationProcessing.remove(node);
if (deferredProcessing != null) {
@@ -681,7 +683,7 @@
}
}
var hint =
- _fixBuilder._variables.getNullCheckHint(_fixBuilder.source, node);
+ _fixBuilder!._variables!.getNullCheckHint(_fixBuilder!.source, node);
if (hint != null) {
type = _addNullCheck(node, type,
info: AtomicEditInfo(
@@ -697,35 +699,35 @@
var ancestor = _findNullabilityContextAncestor(node);
context ??=
InferenceContext.getContext(ancestor) ?? DynamicTypeImpl.instance;
- if (!_fixBuilder._typeSystem.isSubtypeOf(type, context)) {
+ if (!_fixBuilder!._typeSystem.isSubtypeOf(type, context)) {
var transformationInfo =
- _fixBuilder._whereOrNullTransformer.tryTransformOrElseArgument(node);
+ _fixBuilder!._whereOrNullTransformer.tryTransformOrElseArgument(node);
if (transformationInfo != null) {
// We can fix this by dropping the node and changing the method call.
- _fixBuilder.needsIterableExtension = true;
- _fixBuilder._neededPackages['collection'] =
+ _fixBuilder!.needsIterableExtension = true;
+ _fixBuilder!._neededPackages['collection'] =
Version.parse('1.15.0-nullsafety.4');
var info = AtomicEditInfo(
NullabilityFixDescription.changeMethodName(
transformationInfo.originalName,
transformationInfo.replacementName),
{});
- (_fixBuilder._getChange(transformationInfo.methodInvocation.methodName)
+ (_fixBuilder!._getChange(transformationInfo.methodInvocation.methodName)
as NodeChangeForMethodName)
.replaceWith(transformationInfo.replacementName, info);
- (_fixBuilder._getChange(
+ (_fixBuilder!._getChange(
transformationInfo.methodInvocation.argumentList)
as NodeChangeForArgumentList)
.dropArgument(transformationInfo.orElseArgument, info);
_deferredMethodInvocationProcessing[
transformationInfo.methodInvocation] =
- (methodInvocationType) => _fixBuilder._typeSystem
+ (methodInvocationType) => _fixBuilder!._typeSystem
.makeNullable(methodInvocationType as TypeImpl);
return type;
}
return _addCastOrNullCheck(node, type, context);
}
- if (!_fixBuilder._typeSystem.isNullable(type)) return type;
+ if (!_fixBuilder!._typeSystem.isNullable(type)) return type;
if (_needsNullCheckDueToStructure(ancestor)) {
return _addNullCheck(node, type);
}
@@ -766,7 +768,7 @@
return false;
}
// TODO(paulberry): what about cascaded?
- return parent.operator.type == TokenType.PERIOD &&
+ return parent.operator!.type == TokenType.PERIOD &&
identical(node, parent.target);
} else if (parent is IndexExpression) {
if (identical(node, parent.target)) {
@@ -792,16 +794,16 @@
return false;
}
- CollectionElement _transformCollectionElement(CollectionElement node) {
+ CollectionElement? _transformCollectionElement(CollectionElement? node) {
while (node is IfElement) {
- var conditionalDiscard = _fixBuilder._variables
- .getConditionalDiscard(_fixBuilder.source, node);
+ var conditionalDiscard = _fixBuilder!._variables!
+ .getConditionalDiscard(_fixBuilder!.source, node);
if (conditionalDiscard == null ||
conditionalDiscard.keepTrue && conditionalDiscard.keepFalse) {
return node;
}
var conditionValue = conditionalDiscard.keepTrue;
- var ifElement = node as IfElement;
+ var ifElement = node;
node = conditionValue ? ifElement.thenElement : ifElement.elseElement;
}
return node;
@@ -811,7 +813,7 @@
NodeList<CollectionElement> elements) {
return elements
.map(_transformCollectionElement)
- .where((e) => e != null)
+ .whereType<CollectionElement>()
.toList();
}
@@ -820,13 +822,13 @@
/// listener and [fallback] is called to produce a result. Otherwise the
/// exception is propagated normally.
T _wrapExceptions<T>(
- AstNode node, T Function() fallback, T Function() compute) {
- if (_fixBuilder.listener == null) return compute();
+ AstNode? node, T Function() fallback, T Function() compute) {
+ if (_fixBuilder!.listener == null) return compute();
try {
return compute();
} catch (exception, stackTrace) {
- _fixBuilder.listener
- .reportException(_fixBuilder.source, node, exception, stackTrace);
+ _fixBuilder!.listener!
+ .reportException(_fixBuilder!.source, node, exception, stackTrace);
return fallback();
}
}
@@ -850,7 +852,7 @@
_AssignmentExpressionHandler(this.node);
@override
- MethodElement get combiner => node.staticElement;
+ MethodElement? get combiner => node.staticElement;
@override
TokenType get combinerType => node.operator.type;
@@ -864,16 +866,16 @@
/// assignment.
abstract class _AssignmentLikeExpressionHandler {
/// For compound and null-aware assignments, the type read from the LHS.
- /*late final*/ DartType readType;
+ late final DartType? readType;
/// The type that may be written to the LHS.
- /*late final*/ DartType writeType;
+ late final DartType writeType;
/// The type that should be used as a context type when inferring the RHS.
- DartType rhsContextType;
+ DartType? rhsContextType;
/// Gets the static element representing the combiner.
- MethodElement get combiner;
+ MethodElement? get combiner;
/// Gets the operator type representing the combiner.
TokenType get combinerType;
@@ -889,12 +891,12 @@
/// [writeType].
void handleAssignmentRhs(
MigrationResolutionHooksImpl hooks, DartType rhsType) {
- MethodElement combiner = this.combiner;
+ MethodElement? combiner = this.combiner;
if (combiner != null) {
- var fixBuilder = hooks._fixBuilder;
+ var fixBuilder = hooks._fixBuilder!;
var combinerReturnType =
fixBuilder._typeSystem.refineBinaryExpressionType(
- readType,
+ readType!,
combinerType,
rhsType,
combiner.returnType,
@@ -912,7 +914,7 @@
/// assignments, the [readType] is non-nullable, and that for null-aware
/// assignments, the [readType] is nullable.
void handleLValueType(MigrationResolutionHooksImpl hooks,
- DartType readTypeToSet, DartType writeTypeToSet) {
+ DartType? readTypeToSet, DartType writeTypeToSet) {
assert(writeTypeToSet.nullabilitySuffix != NullabilitySuffix.star);
writeType = writeTypeToSet;
// TODO(scheglov) Remove this after the analyzer breaking change that
@@ -923,16 +925,16 @@
rhsContextType = writeTypeToSet;
} else {
readType = readTypeToSet;
- assert(readType.nullabilitySuffix != NullabilitySuffix.star);
+ assert(readType!.nullabilitySuffix != NullabilitySuffix.star);
if (combinerType == TokenType.QUESTION_QUESTION_EQ) {
rhsContextType = writeTypeToSet;
- if (fixBuilder._typeSystem.isNonNullable(readType)) {
+ if (fixBuilder!._typeSystem.isNonNullable(readType!)) {
(fixBuilder._getChange(node) as NodeChangeForAssignment)
.isWeakNullAware = true;
}
} else {
- if (!readType.isDynamic &&
- fixBuilder._typeSystem.isPotentiallyNullable(readType)) {
+ if (!readType!.isDynamic &&
+ fixBuilder!._typeSystem.isPotentiallyNullable(readType!)) {
(fixBuilder._getChange(node) as NodeChangeForAssignmentLike)
.hasNullableSource = true;
}
@@ -959,14 +961,14 @@
_FixBuilderPostVisitor(this._fixBuilder);
@override
- NullabilityMigrationListener get listener => _fixBuilder.listener;
+ NullabilityMigrationListener? get listener => _fixBuilder.listener;
@override
- Source get source => _fixBuilder.source;
+ Source? get source => _fixBuilder.source;
@override
void visitAsExpression(AsExpression node) {
- if (!_fixBuilder._variables.wasUnnecessaryCast(_fixBuilder.source, node) &&
+ if (!_fixBuilder._variables!.wasUnnecessaryCast(_fixBuilder.source, node) &&
BestPracticesVerifier.isUnnecessaryCast(
node, _fixBuilder._typeSystem)) {
(_fixBuilder._getChange(node) as NodeChangeForAsExpression).removeAs =
@@ -1001,7 +1003,7 @@
@override
void visitSimpleFormalParameter(SimpleFormalParameter node) {
if (node.type == null) {
- var typeToAdd = _fixBuilder._addedParameterTypes[node.declaredElement];
+ var typeToAdd = _fixBuilder._addedParameterTypes[node.declaredElement!];
if (typeToAdd != null) {
(_fixBuilder._getChange(node) as NodeChangeForSimpleFormalParameter)
.addExplicitType = typeToAdd;
@@ -1018,7 +1020,7 @@
bool explicitTypeNeeded = false;
for (var variableDeclaration in node.variables) {
var neededType = _fixBuilder
- ._computeMigratedType(variableDeclaration.declaredElement);
+ ._computeMigratedType(variableDeclaration.declaredElement!);
neededTypes.add(neededType);
var inferredType = variableDeclaration.initializer?.staticType ??
_fixBuilder.typeProvider.dynamicType;
@@ -1042,10 +1044,10 @@
// Check if the nullability node for a single variable declaration has been
// declared to be late.
if (node.variables.length == 1) {
- var variableElement = node.variables.single.declaredElement;
- var lateCondition = _fixBuilder._variables
+ var variableElement = node.variables.single.declaredElement!;
+ var lateCondition = _fixBuilder._variables!
.decoratedElementType(variableElement)
- .node
+ .node!
.lateCondition;
switch (lateCondition) {
case LateCondition.possiblyLate:
@@ -1063,7 +1065,7 @@
}
}
- var lateHint = _fixBuilder._variables.getLateHint(source, node);
+ var lateHint = _fixBuilder._variables!.getLateHint(source, node);
if (lateHint != null) {
(_fixBuilder._getChange(node) as NodeChangeForVariableDeclarationList)
.lateHint = lateHint;
@@ -1082,7 +1084,7 @@
/// Searches [unit] for an unprefixed import directive whose URI matches
/// [uri], returning it if found, or `null` if not found.
- ImportDirective _findImportDirective(CompilationUnit unit, String uri) {
+ ImportDirective? _findImportDirective(CompilationUnit unit, String uri) {
for (var directive in unit.directives) {
if (directive is ImportDirective &&
directive.prefix == null &&
@@ -1104,19 +1106,19 @@
_FixBuilderPreVisitor(this._fixBuilder);
@override
- NullabilityMigrationListener get listener => _fixBuilder.listener;
+ NullabilityMigrationListener? get listener => _fixBuilder.listener;
@override
- Source get source => _fixBuilder.source;
+ Source? get source => _fixBuilder.source;
@override
void visitDefaultFormalParameter(DefaultFormalParameter node) {
var element = node.declaredElement;
if (node.defaultValue == null) {
var requiredHint =
- _fixBuilder._variables.getRequiredHint(_fixBuilder.source, node);
+ _fixBuilder._variables!.getRequiredHint(_fixBuilder.source, node);
var nullabilityNode =
- _fixBuilder._variables.decoratedElementType(element).node;
+ _fixBuilder._variables!.decoratedElementType(element!).node!;
if (!nullabilityNode.isNullable) {
var enclosingElement = element.enclosingElement;
if (enclosingElement is ConstructorElement &&
@@ -1144,12 +1146,12 @@
if (node.type == null) {
// Potentially add an explicit type to a field formal parameter.
var decl = node.declaredElement as FieldFormalParameterElement;
- var decoratedType = _fixBuilder._variables.decoratedElementType(decl);
+ var decoratedType = _fixBuilder._variables!.decoratedElementType(decl);
var decoratedFieldType =
- _fixBuilder._variables.decoratedElementType(decl.field);
- var typeToAdd = _fixBuilder._variables.toFinalType(decoratedType);
+ _fixBuilder._variables!.decoratedElementType(decl.field!);
+ var typeToAdd = _fixBuilder._variables!.toFinalType(decoratedType);
var fieldFinalType =
- _fixBuilder._variables.toFinalType(decoratedFieldType);
+ _fixBuilder._variables!.toFinalType(decoratedFieldType);
if (typeToAdd is InterfaceType &&
!_fixBuilder._typeSystem.isSubtypeOf(fieldFinalType, typeToAdd)) {
(_fixBuilder._getChange(node) as NodeChangeForFieldFormalParameter)
@@ -1158,12 +1160,12 @@
} else if (node.parameters != null) {
// Handle function-typed field formal parameters.
var decoratedType =
- _fixBuilder._variables.decoratedElementType(node.declaredElement);
- if (decoratedType.node.isNullable) {
+ _fixBuilder._variables!.decoratedElementType(node.declaredElement!);
+ if (decoratedType.node!.isNullable) {
(_fixBuilder._getChange(node) as NodeChangeForFieldFormalParameter)
- .recordNullability(decoratedType, decoratedType.node.isNullable,
+ .recordNullability(decoratedType, decoratedType.node!.isNullable,
nullabilityHint:
- _fixBuilder._variables.getNullabilityHint(source, node));
+ _fixBuilder._variables!.getNullabilityHint(source, node));
}
}
super.visitFieldFormalParameter(node);
@@ -1172,60 +1174,60 @@
@override
void visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
var decoratedType =
- _fixBuilder._variables.decoratedElementType(node.declaredElement);
- if (decoratedType.node.isNullable) {
+ _fixBuilder._variables!.decoratedElementType(node.declaredElement!);
+ if (decoratedType.node!.isNullable) {
(_fixBuilder._getChange(node)
as NodeChangeForFunctionTypedFormalParameter)
- .recordNullability(decoratedType, decoratedType.node.isNullable,
+ .recordNullability(decoratedType, decoratedType.node!.isNullable,
nullabilityHint:
- _fixBuilder._variables.getNullabilityHint(source, node));
+ _fixBuilder._variables!.getNullabilityHint(source, node));
}
super.visitFunctionTypedFormalParameter(node);
}
@override
void visitGenericFunctionType(GenericFunctionType node) {
- var decoratedType = _fixBuilder._variables
+ var decoratedType = _fixBuilder._variables!
.decoratedTypeAnnotation(_fixBuilder.source, node);
if (!typeIsNonNullableByContext(node)) {
_makeTypeNameNullable(node, decoratedType);
}
(node as GenericFunctionTypeImpl).type =
- _fixBuilder._variables.toFinalType(decoratedType);
+ _fixBuilder._variables!.toFinalType(decoratedType);
super.visitGenericFunctionType(node);
}
@override
void visitTypeName(TypeName node) {
- var decoratedType = _fixBuilder._variables
+ var decoratedType = _fixBuilder._variables!
.decoratedTypeAnnotation(_fixBuilder.source, node);
if (!typeIsNonNullableByContext(node)) {
- if (!_typeIsNaturallyNullable(decoratedType.type)) {
+ if (!_typeIsNaturallyNullable(decoratedType.type!)) {
_makeTypeNameNullable(node, decoratedType);
}
}
(node as TypeNameImpl).type =
- _fixBuilder._variables.toFinalType(decoratedType);
+ _fixBuilder._variables!.toFinalType(decoratedType);
super.visitTypeName(node);
}
void _addRequiredKeyword(DefaultFormalParameter parameter,
- NullabilityNode node, HintComment requiredHint) {
+ NullabilityNode node, HintComment? requiredHint) {
// Change an existing `@required` annotation into a `required` keyword if
// possible.
- final element = parameter.declaredElement;
- final method = element.enclosingElement;
- final cls = method.enclosingElement;
+ final element = parameter.declaredElement!;
+ final method = element.enclosingElement!;
+ final cls = method.enclosingElement!;
var info = AtomicEditInfo(
NullabilityFixDescription.addRequired(
cls.name, method.name, element.name),
{FixReasonTarget.root: node});
var metadata = parameter.metadata;
- if (metadata != null && metadata.isNotEmpty) {
+ if (metadata.isNotEmpty) {
// Only the last annotation can be changed into a `required` keyword;
// changing an earlier annotation into a keyword would be illegal.
var lastAnnotation = metadata.last;
- if (lastAnnotation.elementAnnotation.isRequired) {
+ if (lastAnnotation.elementAnnotation!.isRequired) {
(_fixBuilder._getChange(lastAnnotation) as NodeChangeForAnnotation)
..changeToRequiredKeyword = true
..changeToRequiredKeywordInfo = info;
@@ -1238,9 +1240,8 @@
..addRequiredKeyword = true
..addRequiredKeywordInfo = info
..requiredHint = requiredHint;
- var requiredAnnotation = metadata?.firstWhere(
- (annotation) => annotation.elementAnnotation.isRequired,
- orElse: () => null);
+ var requiredAnnotation = metadata.firstWhereOrNull(
+ (annotation) => annotation.elementAnnotation!.isRequired);
if (requiredAnnotation != null) {
// If the parameter was annotated with `@required`, but it was not the
// last annotation, we remove the annotation in addition to adding the
@@ -1252,13 +1253,13 @@
}
void _makeTypeNameNullable(TypeAnnotation node, DecoratedType decoratedType) {
- bool makeNullable = decoratedType.node.isNullable;
- if (decoratedType.type.isDartAsyncFutureOr) {
+ bool makeNullable = decoratedType.node!.isNullable;
+ if (decoratedType.type!.isDartAsyncFutureOr) {
var typeArguments = decoratedType.typeArguments;
if (typeArguments.length == 1) {
- var typeArgument = typeArguments[0];
- if ((_typeIsNaturallyNullable(typeArgument.type) ||
- typeArgument.node.isNullable)) {
+ var typeArgument = typeArguments[0]!;
+ if ((_typeIsNaturallyNullable(typeArgument.type!) ||
+ typeArgument.node!.isNullable)) {
// FutureOr<T?>? is equivalent to FutureOr<T?>, so there is no need to
// make this type nullable.
makeNullable = false;
@@ -1269,7 +1270,7 @@
.recordNullability(
decoratedType, makeNullable,
nullabilityHint:
- _fixBuilder._variables.getNullabilityHint(source, node));
+ _fixBuilder._variables!.getNullabilityHint(source, node));
}
bool _typeIsNaturallyNullable(DartType type) =>
@@ -1286,7 +1287,7 @@
: assert(_isIncrementOrDecrementOperator(node.operator.type));
@override
- MethodElement get combiner => node.staticElement;
+ MethodElement? get combiner => node.staticElement;
@override
TokenType get combinerType => node.operator.type;
@@ -1305,7 +1306,7 @@
: assert(_isIncrementOrDecrementOperator(node.operator.type));
@override
- MethodElement get combiner => node.staticElement;
+ MethodElement? get combiner => node.staticElement;
@override
TokenType get combinerType => node.operator.type;
diff --git a/pkg/nnbd_migration/lib/src/front_end/dartfix_listener.dart b/pkg/nnbd_migration/lib/src/front_end/dartfix_listener.dart
index 4c6fa9c..6aa3866 100644
--- a/pkg/nnbd_migration/lib/src/front_end/dartfix_listener.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/dartfix_listener.dart
@@ -5,12 +5,11 @@
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart'
hide AnalysisError;
-import 'package:meta/meta.dart';
import 'package:nnbd_migration/src/front_end/driver_provider_impl.dart';
import 'package:pub_semver/src/version.dart';
class DartFixListener {
- final DriverProviderImpl server;
+ final DriverProviderImpl? server;
final SourceChange sourceChange = SourceChange('null safety migration');
@@ -80,5 +79,5 @@
final Location location;
- DartFixSuggestion(this.description, {@required this.location});
+ DartFixSuggestion(this.description, {required this.location});
}
diff --git a/pkg/nnbd_migration/lib/src/front_end/driver_provider_impl.dart b/pkg/nnbd_migration/lib/src/front_end/driver_provider_impl.dart
index 340d5c7..ac3c21f 100644
--- a/pkg/nnbd_migration/lib/src/front_end/driver_provider_impl.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/driver_provider_impl.dart
@@ -9,12 +9,12 @@
class DriverProviderImpl {
final ResourceProvider resourceProvider;
- final AnalysisContext analysisContext;
+ final AnalysisContext? analysisContext;
DriverProviderImpl(this.resourceProvider, this.analysisContext);
/// Return the appropriate analysis session for the file with the given
/// [path].
- AnalysisSession getAnalysisSession(String path) =>
- analysisContext.currentSession;
+ AnalysisSession getAnalysisSession(String? path) =>
+ analysisContext!.currentSession;
}
diff --git a/pkg/nnbd_migration/lib/src/front_end/info_builder.dart b/pkg/nnbd_migration/lib/src/front_end/info_builder.dart
index 75bdd5b..c6f6fb4 100644
--- a/pkg/nnbd_migration/lib/src/front_end/info_builder.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/info_builder.dart
@@ -31,7 +31,7 @@
/// A builder used to build the migration information for a library.
class InfoBuilder {
/// The node mapper for the migration state.
- NodeMapper nodeMapper;
+ NodeMapper? nodeMapper;
/// The logger to use for showing progress when explaining the migration.
final Logger _logger;
@@ -39,28 +39,28 @@
/// The resource provider used to access the file system.
ResourceProvider provider;
- String includedPath;
+ String? includedPath;
/// The instrumentation information gathered while the migration engine was
/// running.
final InstrumentationInformation info;
/// The listener used to gather the changes to be applied.
- final DartFixListener listener;
+ final DartFixListener? listener;
/// The [NullabilityMigration] instance for this migration.
- final NullabilityMigration migration;
+ final NullabilityMigration? migration;
/// A map from the path of a compilation unit to the information about that
/// unit.
- final Map<String, UnitInfo> unitMap = {};
+ final Map<String?, UnitInfo> unitMap = {};
/// A function which returns whether a file at a given path should be
/// migrated.
- final bool Function(String) shouldBeMigratedFunction;
+ final bool Function(String?) shouldBeMigratedFunction;
/// The set of files which are being considered for migration.
- final Iterable<String> _pathsToProcess;
+ final Iterable<String?>? _pathsToProcess;
/// Initialize a newly created builder.
InfoBuilder(
@@ -75,32 +75,32 @@
this._pathsToProcess);
/// The provider used to get information about libraries.
- DriverProviderImpl get driverProvider => listener.server;
+ DriverProviderImpl? get driverProvider => listener!.server;
/// Return the migration information for all of the libraries that were
/// migrated.
Future<Set<UnitInfo>> explainMigration() async {
var sourceInfoMap = info.sourceInformation;
Set<UnitInfo> units =
- SplayTreeSet<UnitInfo>((u1, u2) => u1.path.compareTo(u2.path));
+ SplayTreeSet<UnitInfo>((u1, u2) => u1.path!.compareTo(u2.path!));
// Collect all of the sources for which we have [SourceInformation], as well
// as all files which are being "processed" during this migration, which may
// include already migrated files.
var sources = {
- ...sourceInfoMap.keys.map((source) => source.fullName),
- ..._pathsToProcess,
+ ...sourceInfoMap.keys.map((source) => source!.fullName),
+ ..._pathsToProcess!,
};
var progressBar = ProgressBar(_logger, sources.length);
for (var filePath in sources) {
progressBar.tick();
- var session = driverProvider.getAnalysisSession(filePath);
- var result = await session.getResolvedLibrary2(filePath);
+ var session = driverProvider!.getAnalysisSession(filePath);
+ var result = await session.getResolvedLibrary2(filePath!);
if (result is ResolvedLibraryResult) {
- for (var unitResult in result.units) {
+ for (var unitResult in result.units!) {
var sourceInfo =
- sourceInfoMap[unitResult.unit.declaredElement.source];
+ sourceInfoMap[unitResult.unit!.declaredElement!.source];
// Note: there might have been no information for this unit in
// sourceInfoMap. That can happen if there's an already-migrated
// library being referenced by the code being migrated, but not all
@@ -111,9 +111,9 @@
// referenced (we'll just skip the entire library because we'll only
// ever see its parts).
sourceInfo ??= SourceInformation();
- var edit = listener.sourceChange.getFileEdit(unitResult.path);
+ var edit = listener!.sourceChange.getFileEdit(unitResult.path!);
var unit = _explainUnit(sourceInfo, unitResult, edit);
- if (_pathsToProcess.contains(unitResult.path)) {
+ if (_pathsToProcess!.contains(unitResult.path)) {
units.add(unit);
}
}
@@ -129,7 +129,7 @@
for (var edge in node.upstreamEdges) {
if (skipExactNullable &&
node.isExactNullable &&
- edge.sourceNode.isExactNullable) {
+ edge.sourceNode!.isExactNullable) {
// When an exact nullable points here, the nullability propagated
// in the other direction.
continue;
@@ -164,12 +164,12 @@
EditDetail removeHint(String description) => EditDetail.fromSourceEdit(
description,
- fixInfo.hintComment.changesToRemove(content).toSourceEdits().single);
+ fixInfo.hintComment!.changesToRemove(content).toSourceEdits().single);
EditDetail changeHint(String description, String replacement) =>
EditDetail.fromSourceEdit(
description,
- fixInfo.hintComment
+ fixInfo.hintComment!
.changesToReplace(content, replacement)
.toSourceEdits()
.single);
@@ -185,7 +185,7 @@
break;
case NullabilityFixKind.addRequired:
var metaImport =
- _findImportDirective(result.unit, 'package:meta/meta.dart');
+ _findImportDirective(result.unit!, 'package:meta/meta.dart');
if (metaImport == null) {
edits.add(
EditDetail('Add /*required*/ hint', offset, 0, '/*required*/ '));
@@ -273,13 +273,13 @@
List<NavigationSource> _computeNavigationSources(ResolvedUnitResult result) {
var collector = NavigationCollectorImpl();
computeDartNavigation(
- result.session.resourceProvider, collector, result.unit, null, null);
+ result.session.resourceProvider, collector, result.unit!, null, null);
collector.createRegions();
var files = collector.files;
var regions = collector.regions;
var rawTargets = collector.targets;
var convertedTargets =
- List<NavigationTarget>.filled(rawTargets.length, null);
+ List<NavigationTarget?>.filled(rawTargets.length, null);
return regions.map((region) {
var targets = region.targets;
if (targets.isEmpty) {
@@ -331,7 +331,7 @@
}
assert(identical(step.targetNode, node));
while (step != null) {
- entries.add(_nodeToTraceEntry(step.targetNode));
+ entries.add(_nodeToTraceEntry(step.targetNode!));
if (step.codeReference != null) {
entries.add(_stepToTraceEntry(step));
}
@@ -342,7 +342,7 @@
}
List<TraceInfo> _computeTraces(
- Map<FixReasonTarget, FixReasonInfo> fixReasons) {
+ Map<FixReasonTarget?, FixReasonInfo?> fixReasons) {
var traces = <TraceInfo>[];
for (var entry in fixReasons.entries) {
var reason = entry.value;
@@ -353,10 +353,10 @@
_computeTraceNonNullableInfo(reason, traces, FixReasonTarget.root);
}
} else if (reason is EdgeInfo) {
- if (reason.sourceNode.isNullable &&
+ if (reason.sourceNode!.isNullable &&
!reason.destinationNode.isNullable) {
- var target = entry.key;
- _computeTraceNullableInfo(reason.sourceNode, traces, target);
+ var target = entry.key!;
+ _computeTraceNullableInfo(reason.sourceNode!, traces, target);
_computeTraceNonNullableInfo(reason.destinationNode, traces, target);
}
} else if (reason is SimpleFixReasonInfo) {
@@ -371,14 +371,14 @@
/// Return the migration information for the unit associated with the
/// [result].
UnitInfo _explainUnit(SourceInformation sourceInfo, ResolvedUnitResult result,
- SourceFileEdit fileEdit) {
+ SourceFileEdit? fileEdit) {
var unitInfo = _unitForPath(result.path);
unitInfo.sources ??= _computeNavigationSources(result);
var content = result.content;
unitInfo.diskContent = content;
var alreadyMigrated =
- result.unit.featureSet.isEnabled(Feature.non_nullable);
- unitInfo.wasExplicitlyOptedOut = result.unit.languageVersionToken != null;
+ result.unit!.featureSet.isEnabled(Feature.non_nullable);
+ unitInfo.wasExplicitlyOptedOut = result.unit!.languageVersionToken != null;
if (alreadyMigrated) {
unitInfo.migrationStatus = UnitMigrationStatus.alreadyMigrated;
unitInfo.migrationStatusCanBeChanged = false;
@@ -401,8 +401,8 @@
// before we re-populate the region list.
regions.clear();
- var lineInfo = result.unit.lineInfo;
- var insertions = <int, List<AtomicEdit>>{};
+ var lineInfo = result.unit!.lineInfo;
+ var insertions = <int?, List<AtomicEdit>>{};
var infosSeen = Set<AtomicEditInfo>.identity();
// Apply edits and build the regions.
@@ -412,8 +412,8 @@
var offset = 0;
var sourceOffset = 0;
for (var nextSourceOffset in sourceOffsets) {
- var changesForSourceOffset = changes[nextSourceOffset];
- var unchangedTextLength = nextSourceOffset - sourceOffset;
+ var changesForSourceOffset = changes[nextSourceOffset]!;
+ var unchangedTextLength = nextSourceOffset! - sourceOffset;
offset += unchangedTextLength;
sourceOffset += unchangedTextLength;
for (var edit in changesForSourceOffset) {
@@ -422,14 +422,14 @@
var end = offset + length;
// Insert the replacement text without deleting the replaced text.
if (replacement.isNotEmpty) {
- content = content.replaceRange(end, end, replacement);
+ content = content!.replaceRange(end, end, replacement);
(insertions[sourceOffset] ??= []).add(AtomicEdit.insert(replacement));
}
var info = edit.info;
var edits = info != null
? _computeEdits(info, sourceOffset, result)
: <EditDetail>[];
- var lineNumber = lineInfo.getLocation(sourceOffset).lineNumber;
+ var lineNumber = lineInfo!.getLocation(sourceOffset).lineNumber;
var traces = info == null
? const <TraceInfo>[]
: _computeTraces(info.fixReasons);
@@ -491,7 +491,7 @@
/// Searches [unit] for an import directive whose URI matches [uri], returning
/// it if found, or `null` if not found.
- ImportDirective _findImportDirective(CompilationUnit unit, String uri) {
+ ImportDirective? _findImportDirective(CompilationUnit unit, String uri) {
for (var directive in unit.directives) {
if (directive is ImportDirective && directive.uriContent == uri) {
return directive;
@@ -501,7 +501,7 @@
}
TraceEntryInfo _makeTraceEntry(
- String description, CodeReference codeReference,
+ String description, CodeReference? codeReference,
{List<HintAction> hintActions = const []}) {
var length = 1; // TODO(paulberry): figure out the correct value.
return TraceEntryInfo(
@@ -515,11 +515,11 @@
}
TraceEntryInfo _nodeToTraceEntry(NullabilityNodeInfo node,
- {String description}) {
+ {String? description}) {
description ??= node.toString(); // TODO(paulberry): improve this message
return _makeTraceEntry(description, node.codeReference,
hintActions: node.hintActions.keys
- .map((kind) => HintAction(kind, nodeMapper.idForNode(node)))
+ .map((kind) => HintAction(kind, nodeMapper!.idForNode(node)))
.toList());
}
@@ -542,7 +542,7 @@
}
/// Return the unit info for the file at the given [path].
- UnitInfo _unitForPath(String path) {
+ UnitInfo _unitForPath(String? path) {
return unitMap.putIfAbsent(path, () => UnitInfo(path));
}
@@ -551,21 +551,21 @@
/// This may include a class and method name, for example, or the name of the
/// enclosing top-level member.
@visibleForTesting
- static String buildEnclosingMemberDescription(AstNode node) {
+ static String buildEnclosingMemberDescription(AstNode? node) {
for (var enclosingNode = node;
enclosingNode != null;
enclosingNode = enclosingNode.parent) {
if (enclosingNode is ConstructorDeclaration) {
if (enclosingNode.name == null) {
return _describeClassOrExtensionMember(
- enclosingNode.parent as CompilationUnitMember,
+ enclosingNode.parent as CompilationUnitMember?,
'the default constructor of',
'');
} else {
return _describeClassOrExtensionMember(
- enclosingNode.parent as CompilationUnitMember,
+ enclosingNode.parent as CompilationUnitMember?,
'the constructor',
- enclosingNode.name.name);
+ enclosingNode.name!.name);
}
} else if (enclosingNode is MethodDeclaration) {
var functionName = enclosingNode.name.name;
@@ -581,7 +581,7 @@
baseDescription = 'the method';
}
return _describeClassOrExtensionMember(
- enclosingNode.parent as CompilationUnitMember,
+ enclosingNode.parent as CompilationUnitMember?,
baseDescription,
functionName);
} else if (enclosingNode is FunctionDeclaration &&
@@ -610,7 +610,7 @@
"Can't describe enclosing member of ${node.runtimeType}");
}
- static String _describeClassOrExtensionMember(CompilationUnitMember parent,
+ static String _describeClassOrExtensionMember(CompilationUnitMember? parent,
String baseDescription, String functionName) {
if (parent is NamedCompilationUnitMember) {
var parentName = parent.name.name;
@@ -621,12 +621,12 @@
}
} else if (parent is ExtensionDeclaration) {
if (parent.name == null) {
- var extendedTypeString = parent.extendedType.type.getDisplayString(
+ var extendedTypeString = parent.extendedType.type!.getDisplayString(
withNullability: false,
);
return "$baseDescription '$functionName' in unnamed extension on $extendedTypeString";
} else {
- return "$baseDescription '${parent.name.name}.$functionName'";
+ return "$baseDescription '${parent.name!.name}.$functionName'";
}
} else {
throw ArgumentError(
@@ -634,13 +634,13 @@
}
}
- static String _describeVariableDeclaration(VariableDeclaration node) {
+ static String? _describeVariableDeclaration(VariableDeclaration node) {
var variableName = node.name.name;
- var parent = node.parent;
+ var parent = node.parent!;
var grandParent = parent.parent;
if (grandParent is FieldDeclaration) {
return _describeClassOrExtensionMember(
- grandParent.parent as CompilationUnitMember,
+ grandParent.parent as CompilationUnitMember?,
'the field',
variableName);
} else if (grandParent is TopLevelVariableDeclaration) {
diff --git a/pkg/nnbd_migration/lib/src/front_end/instrumentation_information.dart b/pkg/nnbd_migration/lib/src/front_end/instrumentation_information.dart
index 6099a8f..532b4ed 100644
--- a/pkg/nnbd_migration/lib/src/front_end/instrumentation_information.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/instrumentation_information.dart
@@ -11,24 +11,24 @@
/// The instrumentation information gathered from the migration engine.
class InstrumentationInformation {
/// The node used for type sources that are always `null`.
- NullabilityNodeInfo always;
+ NullabilityNodeInfo? always;
/// A map from the graph edges between nullability nodes, to information about
/// the edge that was created and why it was created.
final Map<EdgeInfo, EdgeOriginInfo> edgeOrigin = {};
/// The node used for type sources that are never `null`.
- NullabilityNodeInfo never;
+ NullabilityNodeInfo? never;
/// The instrumentation information that is specific to a single source.
- final Map<Source, SourceInformation> sourceInformation = {};
+ final Map<Source?, SourceInformation> sourceInformation = {};
/// Initialize a newly created holder of instrumentation information.
InstrumentationInformation();
/// Return the type annotation associated with the [node] or `null` if the
/// node represents an implicit type.
- TypeAnnotation typeAnnotationForNode(NullabilityNodeInfo node) {
+ TypeAnnotation? typeAnnotationForNode(NullabilityNodeInfo node) {
for (var sourceEntry in sourceInformation.entries) {
for (var typeEntry in sourceEntry.value.explicitTypeNullability.entries) {
if (typeEntry.value == node) {
@@ -65,11 +65,11 @@
/// nullability nodes that are associated with that type.
///
/// TODO(paulberry): we should probably get rid of this data structure.
- final Map<TypeAnnotation, NullabilityNodeInfo> explicitTypeNullability = {};
+ final Map<TypeAnnotation, NullabilityNodeInfo?> explicitTypeNullability = {};
/// A map from offsets within the source file to a list of changes to be
/// applied at that offset.
- Map<int, List<AtomicEdit>> changes;
+ Map<int?, List<AtomicEdit>>? changes;
/// Initialize a newly created holder of instrumentation information that is
/// specific to a single source.
diff --git a/pkg/nnbd_migration/lib/src/front_end/instrumentation_listener.dart b/pkg/nnbd_migration/lib/src/front_end/instrumentation_listener.dart
index c6927da..acf0889 100644
--- a/pkg/nnbd_migration/lib/src/front_end/instrumentation_listener.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/instrumentation_listener.dart
@@ -13,7 +13,7 @@
/// A listener used to gather instrumentation information from the migration
/// engine.
class InstrumentationListener implements NullabilityMigrationInstrumentation {
- final MigrationSummary migrationSummary;
+ final MigrationSummary? migrationSummary;
/// The instrumentation information being gathered.
InstrumentationInformation data = InstrumentationInformation();
@@ -22,15 +22,15 @@
InstrumentationListener({this.migrationSummary});
@override
- void changes(Source source, Map<int, List<AtomicEdit>> changes) {
+ void changes(Source source, Map<int?, List<AtomicEdit>> changes) {
assert(_sourceInfo(source).changes == null);
_sourceInfo(source).changes = changes;
migrationSummary?.recordChanges(source, changes);
}
@override
- void explicitTypeNullability(
- Source source, TypeAnnotation typeAnnotation, NullabilityNodeInfo node) {
+ void explicitTypeNullability(Source? source, TypeAnnotation typeAnnotation,
+ NullabilityNodeInfo? node) {
_sourceInfo(source).explicitTypeNullability[typeAnnotation] = node;
}
@@ -60,15 +60,15 @@
@override
void implicitReturnType(
- Source source, AstNode node, DecoratedTypeInfo decoratedReturnType) {}
+ Source? source, AstNode node, DecoratedTypeInfo? decoratedReturnType) {}
@override
void implicitType(
- Source source, AstNode node, DecoratedTypeInfo decoratedType) {}
+ Source? source, AstNode? node, DecoratedTypeInfo decoratedType) {}
@override
void implicitTypeArguments(
- Source source, AstNode node, Iterable<DecoratedTypeInfo> types) {}
+ Source? source, AstNode node, Iterable<DecoratedTypeInfo> types) {}
@override
void prepareForUpdate() {
@@ -79,6 +79,6 @@
/// Return the source information associated with the given [source], creating
/// it if there has been no previous information for that source.
- SourceInformation _sourceInfo(Source source) =>
+ SourceInformation _sourceInfo(Source? source) =>
data.sourceInformation.putIfAbsent(source, () => SourceInformation());
}
diff --git a/pkg/nnbd_migration/lib/src/front_end/instrumentation_renderer.dart b/pkg/nnbd_migration/lib/src/front_end/instrumentation_renderer.dart
index d2abbfc..e7eaf6f 100644
--- a/pkg/nnbd_migration/lib/src/front_end/instrumentation_renderer.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/instrumentation_renderer.dart
@@ -22,17 +22,17 @@
// '2.8.0-edge.fd992e423ef69ece9f44bd3ac58fa2355b563212'
var versionRegExp = RegExp(r'^.*\.([0-9a-f]+)$');
var match = versionRegExp.firstMatch(version);
- if (match != null && match.group(1).length == 40) {
- var commit = match.group(1);
+ if (match != null && match.group(1)!.length == 40) {
+ var commit = match.group(1)!;
version = version.replaceAll(commit, commit.substring(0, 10));
}
return version;
}
-String substituteVariables(String content, Map<String, String> variables) {
+String substituteVariables(String content, Map<String, String?> variables) {
for (var variable in variables.keys) {
- var value = variables[variable];
+ var value = variables[variable]!;
content = content.replaceAll('{{ $variable }}', value);
}
@@ -44,7 +44,7 @@
class InstrumentationRenderer {
/// Information for a whole migration, so that libraries can reference each
/// other.
- final MigrationInfo migrationInfo;
+ final MigrationInfo? migrationInfo;
/// Whether the migration has been applied already or not.
final bool hasBeenApplied;
@@ -54,24 +54,24 @@
/// An object used to map the file paths of analyzed files to the file paths
/// of the HTML files used to view the content of those files.
- final PathMapper pathMapper;
+ final PathMapper? pathMapper;
/// Creates an output object for the given library info.
InstrumentationRenderer(this.migrationInfo, this.pathMapper,
this.hasBeenApplied, this.needsRerun);
/// Returns the path context used to manipulate paths.
- path.Context get pathContext => migrationInfo.pathContext;
+ path.Context get pathContext => migrationInfo!.pathContext;
/// Builds an HTML view of the instrumentation information.
String render() {
- var variables = <String, String>{
- 'root': migrationInfo.includedRoot,
+ var variables = <String, String?>{
+ 'root': migrationInfo!.includedRoot,
'dartPageScript': resources.migration_js,
'dartPageStyle': resources.migration_css,
- 'highlightJsPath': migrationInfo.highlightJsPath,
- 'highlightStylePath': migrationInfo.highlightStylePath,
- 'dartLogoPath': migrationInfo.dartLogoPath,
+ 'highlightJsPath': migrationInfo!.highlightJsPath,
+ 'highlightStylePath': migrationInfo!.highlightStylePath,
+ 'dartLogoPath': migrationInfo!.dartLogoPath,
'sdkVersion': _dartSdkVersion,
'migrationAppliedStyle': hasBeenApplied ? 'applied' : 'proposed',
'needsRerunStyle': needsRerun ? 'needs-rerun' : '',
diff --git a/pkg/nnbd_migration/lib/src/front_end/migration_info.dart b/pkg/nnbd_migration/lib/src/front_end/migration_info.dart
index 9f261cd..bef2c3a 100644
--- a/pkg/nnbd_migration/lib/src/front_end/migration_info.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/migration_info.dart
@@ -46,17 +46,17 @@
/// instrumentation output.
class MigrationInfo {
/// The information about the compilation units that are are migrated.
- final Set<UnitInfo> units;
+ final Set<UnitInfo>? units;
/// A map from file paths to the unit infos created for those files. The units
/// in this map is a strict superset of the [units] that were migrated.
- final Map<String, UnitInfo> unitMap;
+ final Map<String?, UnitInfo> unitMap;
/// The resource provider's path context.
final path.Context pathContext;
/// The filesystem root used to create relative paths for each unit.
- final String includedRoot;
+ final String? includedRoot;
MigrationInfo(this.units, this.unitMap, this.pathContext, this.includedRoot);
@@ -79,8 +79,8 @@
String get robotoMonoFont => PreviewSite.robotoMonoFontPath;
/// Returns the absolute path of [path], as relative to [includedRoot].
- String absolutePathFromRoot(String path) =>
- pathContext.join(includedRoot, path);
+ String absolutePathFromRoot(String? path) =>
+ pathContext.join(includedRoot!, path);
/// Returns the relative path of [path] from [includedRoot].
String relativePathFromRoot(String path) =>
@@ -88,11 +88,11 @@
/// Return the path to [unit] from [includedRoot], to be used as a display
/// name for a library.
- String computeName(UnitInfo unit) => relativePathFromRoot(unit.path);
+ String computeName(UnitInfo unit) => relativePathFromRoot(unit.path!);
List<UnitLink> unitLinks() {
var links = <UnitLink>[];
- for (var unit in units) {
+ for (var unit in units!) {
var count = unit.fixRegions.length;
links.add(UnitLink(
unit.path,
@@ -112,7 +112,7 @@
final int offset;
/// The line number of the region.
- final int line;
+ final int? line;
/// The length of the region.
final int length;
@@ -129,7 +129,7 @@
final NavigationTarget target;
/// Initialize a newly created link.
- NavigationSource(int offset, int line, int length, this.target)
+ NavigationSource(int offset, int? line, int length, this.target)
: super(offset, line, length);
}
@@ -139,7 +139,7 @@
final String filePath;
/// Initialize a newly created anchor.
- NavigationTarget(this.filePath, int offset, int line, int length)
+ NavigationTarget(this.filePath, int offset, int? line, int length)
: super(offset, line, length);
@override
@@ -176,13 +176,13 @@
///
/// `null` if this region doesn't represent a fix (e.g. it's just whitespace
/// change to preserve formatting).
- final String explanation;
+ final String? explanation;
/// The kind of fix that was applied.
///
/// `null` if this region doesn't represent a fix (e.g. it's just whitespace
/// change to preserve formatting).
- final NullabilityFixKind kind;
+ final NullabilityFixKind? kind;
/// Indicates whether this region should be counted in the edit summary.
final bool isCounted;
@@ -219,18 +219,17 @@
final String description;
/// Name of the enclosing function, or `null` if not known.
- String function;
+ String? function;
/// Source code location associated with the entry, or `null` if no source
/// code location is known.
- final NavigationTarget target;
+ final NavigationTarget? target;
/// The hint actions available on this trace entry, or `[]` if none.
final List<HintAction> hintActions;
TraceEntryInfo(this.description, this.function, this.target,
- {this.hintActions = const []})
- : assert(hintActions != null);
+ {this.hintActions = const []});
}
/// Information about a nullability trace.
@@ -247,13 +246,13 @@
/// The migration information associated with a single compilation unit.
class UnitInfo {
/// The absolute and normalized path of the unit.
- final String path;
+ final String? path;
/// Hash of the original contents of the unit.
- List<int> _diskContentHash;
+ List<int>? _diskContentHash;
/// The preview content of unit.
- String content;
+ String? content;
/// The information about the regions that have an explanation associated with
/// them. The offsets in these regions are offsets into the post-edit content.
@@ -261,7 +260,7 @@
/// The navigation sources that are located in this file. The offsets in these
/// sources are offsets into the pre-edit content.
- List<NavigationSource> sources;
+ List<NavigationSource>? sources;
/// The navigation targets that are located in this file. The offsets in these
/// targets are offsets into the pre-edit content.
@@ -277,9 +276,9 @@
/// Whether this compilation unit was explicitly opted out of null safety at
/// the start of this migration.
- /*late*/ bool wasExplicitlyOptedOut;
+ late bool wasExplicitlyOptedOut;
- /*late*/ bool migrationStatusCanBeChanged;
+ late bool migrationStatusCanBeChanged;
/// Indicates the migration status of this unit.
///
@@ -294,14 +293,14 @@
/// * During a follow-up migration, in which a package has been migrated to
/// null safety, but some files have been opted out, the user can toggle a
/// file's migration status between "migrating" and "keeping opted out."
- UnitMigrationStatus migrationStatus;
+ UnitMigrationStatus? migrationStatus;
/// Initialize a newly created unit.
UnitInfo(this.path);
/// Set the original/disk content of this file to later use [hadDiskContent].
/// This does not have a getter because it is backed by a private hash.
- set diskContent(String originalContent) {
+ set diskContent(String? originalContent) {
_diskContentHash = md5.convert((originalContent ?? '').codeUnits).bytes;
}
@@ -323,7 +322,7 @@
OffsetMapper.rebase(diskChangesOffsetMapper, migrationOffsetMapper);
/// Check if this unit's file had expected disk contents [checkContent].
- bool hadDiskContent(String checkContent) {
+ bool hadDiskContent(String? checkContent) {
assert(_diskContentHash != null);
return const ListEquality().equals(
_diskContentHash, md5.convert((checkContent ?? '').codeUnits).bytes);
@@ -340,7 +339,7 @@
throw StateError('cannot apply replacement, offset has been deleted.');
}
try {
- content = content.replaceRange(migratedOffset,
+ content = content!.replaceRange(migratedOffset,
migratedOffset + deleteLength, sourceEdit.replacement);
regions.clear();
regions.addAll(regionsCopy
diff --git a/pkg/nnbd_migration/lib/src/front_end/migration_state.dart b/pkg/nnbd_migration/lib/src/front_end/migration_state.dart
index d65d0cf..033550b 100644
--- a/pkg/nnbd_migration/lib/src/front_end/migration_state.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/migration_state.dart
@@ -18,32 +18,32 @@
bool _hasBeenApplied = false;
/// The migration associated with the state.
- final NullabilityMigration migration;
+ final NullabilityMigration? migration;
/// The root directory that contains all of the files that were migrated.
- final String includedRoot;
+ final String? includedRoot;
/// The mapper user to give nodes ids.
final NodeMapper nodeMapper = SimpleNodeMapper();
/// The listener used to collect fixes.
- final DartFixListener listener;
+ final DartFixListener? listener;
/// The listener that collected information during the migration.
- final InstrumentationListener instrumentationListener;
+ final InstrumentationListener? instrumentationListener;
/// The information that was built from the rest of the migration state.
- MigrationInfo migrationInfo;
+ MigrationInfo? migrationInfo;
/// The object used to map paths.
- PathMapper pathMapper;
+ PathMapper? pathMapper;
/// If there have been changes to disk so the migration needs to be rerun.
bool needsRerun = false;
- final AnalysisResult analysisResult;
+ final AnalysisResult? analysisResult;
- /*late*/ List<String> previewUrls;
+ late List<String>? previewUrls;
/// Map of additional package dependencies that will be required by the
/// migrated code. Keys are package names; values indicate the minimum
@@ -52,7 +52,7 @@
/// A function which returns whether a file at a given path should be
/// migrated.
- final bool Function(String) shouldBeMigratedFunction;
+ final bool Function(String?) shouldBeMigratedFunction;
/// Initialize a newly created migration state with the given values.
MigrationState(
@@ -62,8 +62,7 @@
this.instrumentationListener,
this.neededPackages,
this.shouldBeMigratedFunction,
- [this.analysisResult])
- : assert(neededPackages != null);
+ [this.analysisResult]);
/// If the migration has been applied to disk.
bool get hasBeenApplied => _hasBeenApplied;
@@ -77,13 +76,13 @@
}
/// Refresh the state of the migration after the migration has been updated.
- Future<void> refresh(Logger logger, Iterable<String> pathsToProcess) async {
+ Future<void> refresh(Logger logger, Iterable<String>? pathsToProcess) async {
assert(!hasBeenApplied);
- var provider = listener.server.resourceProvider;
+ var provider = listener!.server!.resourceProvider;
var infoBuilder = InfoBuilder(
provider,
includedRoot,
- instrumentationListener.data,
+ instrumentationListener!.data,
listener,
migration,
nodeMapper,
diff --git a/pkg/nnbd_migration/lib/src/front_end/migration_summary.dart b/pkg/nnbd_migration/lib/src/front_end/migration_summary.dart
index e8e39e1..7a72ee6 100644
--- a/pkg/nnbd_migration/lib/src/front_end/migration_summary.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/migration_summary.dart
@@ -14,7 +14,7 @@
/// migration results.
class MigrationSummary {
/// Path to which the summary should be written.
- final String summaryPath;
+ final String? summaryPath;
final ResourceProvider resourceProvider;
@@ -28,7 +28,7 @@
MigrationSummary(this.summaryPath, this.resourceProvider, this.rootPath);
/// Records information about the [changes] made to a [source] file.
- void recordChanges(Source source, Map<int, List<AtomicEdit>> changes) {
+ void recordChanges(Source source, Map<int?, List<AtomicEdit>> changes) {
var changeSummary = <String, int>{};
var hintsSeen = <HintComment>{};
for (var entry in changes.entries) {
@@ -38,11 +38,8 @@
var hint = info.hintComment;
if (hint == null || hintsSeen.add(hint)) {
var description = info.description;
- if (description != null) {
- var key = _keyForKind(description.kind);
- changeSummary[key] ??= 0;
- changeSummary[key]++;
- }
+ var key = _keyForKind(description.kind);
+ changeSummary[key] = (changeSummary[key] ?? 0) + 1;
}
}
}
@@ -53,7 +50,7 @@
/// Writes out the summary data accumulated so far
void write() {
- resourceProvider.getFile(summaryPath).writeAsStringSync(jsonEncode({
+ resourceProvider.getFile(summaryPath!).writeAsStringSync(jsonEncode({
'changes': {'byPath': _changesByRelativePath}
}));
}
@@ -117,6 +114,5 @@
case NullabilityFixKind.typeNotMadeNullableDueToHint:
return 'typeNotMadeNullableDueToHint';
}
- return '???';
}
}
diff --git a/pkg/nnbd_migration/lib/src/front_end/navigation_tree_renderer.dart b/pkg/nnbd_migration/lib/src/front_end/navigation_tree_renderer.dart
index 86080eb..a733aed 100644
--- a/pkg/nnbd_migration/lib/src/front_end/navigation_tree_renderer.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/navigation_tree_renderer.dart
@@ -22,22 +22,22 @@
/// The HTML that is displayed for a region of code.
class NavigationTreeRenderer {
- final MigrationInfo migrationInfo;
+ final MigrationInfo? migrationInfo;
/// An object used to map the file paths of analyzed files to the file paths
/// of the HTML files used to view the content of those files.
- final PathMapper pathMapper;
+ final PathMapper? pathMapper;
/// Initializes a newly created region page within the given [site]. The
/// [unitInfo] provides the information needed to render the page.
NavigationTreeRenderer(this.migrationInfo, this.pathMapper);
/// Returns the path context used to manipulate paths.
- path.Context get pathContext => migrationInfo.pathContext;
+ path.Context get pathContext => migrationInfo!.pathContext;
/// Renders the navigation link tree.
List<NavigationTreeNode> render() {
- var linkData = migrationInfo.unitLinks();
+ var linkData = migrationInfo!.unitLinks();
var tree = _renderNavigationSubtree(linkData, 0);
for (var node in tree) {
node.parent = null;
@@ -64,7 +64,7 @@
NavigationTreeFileNode(
name: link.fileName,
path: pathContext.joinAll(link.pathParts),
- href: pathMapper.map(link.fullPath),
+ href: pathMapper!.map(link.fullPath!),
editCount: link.editCount,
wasExplicitlyOptedOut: link.wasExplicitlyOptedOut,
migrationStatus: link.migrationStatus,
diff --git a/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart b/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart
index cf5fc65..bed1a35 100644
--- a/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart
@@ -29,18 +29,18 @@
/// and determines whether the associated variable or parameter can be null
/// then adds or removes a '?' trailing the named type as appropriate.
class NonNullableFix {
- static final List<HttpPreviewServer> _allServers = [];
+ static final List<HttpPreviewServer?> _allServers = [];
final Version _intendedMinimumSdkVersion;
/// The internet address the server should bind to. Should be suitable for
/// passing to HttpServer.bind, i.e. either a [String] or an
/// [InternetAddress].
- final Object bindAddress;
+ final Object? bindAddress;
final Logger _logger;
- final int preferredPort;
+ final int? preferredPort;
final DartFixListener listener;
@@ -53,46 +53,46 @@
/// If non-null, the path to which a machine-readable summary of migration
/// results should be written.
- final String summaryPath;
+ final String? summaryPath;
final ResourceProvider resourceProvider;
final LineInfo Function(String) _getLineInfo;
/// The HTTP server that serves the preview tool.
- HttpPreviewServer _server;
+ HttpPreviewServer? _server;
- String authToken;
+ String? authToken;
- InstrumentationListener instrumentationListener;
+ InstrumentationListener? instrumentationListener;
- NullabilityMigrationAdapter adapter;
+ NullabilityMigrationAdapter? adapter;
- NullabilityMigration migration;
+ NullabilityMigration? migration;
- Future<MigrationState> Function() rerunFunction;
+ late Future<MigrationState> Function() rerunFunction;
/// A list of the URLs corresponding to the included roots.
- List<String> previewUrls;
+ List<String>? previewUrls;
/// A function which returns whether a file at a given path should be
/// migrated.
- final bool Function(String) shouldBeMigratedFunction;
+ final bool Function(String?) shouldBeMigratedFunction;
/// The set of files which are being considered for migration.
- Iterable<String> pathsToProcess;
+ Iterable<String>? pathsToProcess;
/// Completes when the server has been shutdown.
- Completer<void> serverIsShutdown;
+ late Completer<void> serverIsShutdown;
NonNullableFix(this.listener, this.resourceProvider, this._getLineInfo,
this.bindAddress, this._logger, this.shouldBeMigratedFunction,
{List<String> included = const [],
this.preferredPort,
this.summaryPath,
- @required String sdkPath})
+ required String sdkPath})
: includedRoot =
- _getIncludedRoot(included, listener.server.resourceProvider),
+ _getIncludedRoot(included, listener.server!.resourceProvider),
_intendedMinimumSdkVersion =
_computeIntendedMinimumSdkVersion(resourceProvider, sdkPath) {
reset();
@@ -108,15 +108,15 @@
'>=$_intendedMinimumSdkVersion <3.0.0';
InstrumentationListener createInstrumentationListener(
- {MigrationSummary migrationSummary}) =>
+ {MigrationSummary? migrationSummary}) =>
InstrumentationListener(migrationSummary: migrationSummary);
Future<void> finalizeUnit(ResolvedUnitResult result) async {
- migration.finalizeInput(result);
+ migration!.finalizeInput(result);
}
Future<MigrationState> finish() async {
- var neededPackages = migration.finish();
+ var neededPackages = migration!.finish();
final state = MigrationState(migration, includedRoot, listener,
instrumentationListener, neededPackages, shouldBeMigratedFunction);
await state.refresh(_logger, pathsToProcess);
@@ -124,7 +124,7 @@
}
Future<void> prepareUnit(ResolvedUnitResult result) async {
- migration.prepareInput(result);
+ migration!.prepareInput(result);
}
/// Processes the non-source files of the package rooted at [pkgFolder].
@@ -161,7 +161,7 @@
}
Future<void> processUnit(ResolvedUnitResult result) async {
- migration.processInput(result);
+ migration!.processInput(result);
}
Future<MigrationState> rerun() async {
@@ -182,7 +182,7 @@
void shutdownServer() {
if (_server != null) {
- _server.close();
+ _server!.close();
_server = null;
serverIsShutdown.complete();
}
@@ -199,11 +199,11 @@
};
_server = HttpPreviewServer(state, rerun, wrappedApplyHookWithShutdown,
bindAddress, preferredPort, _logger);
- _server.serveHttp();
+ _server!.serveHttp();
_allServers.add(_server);
- var serverHostname = await _server.boundHostname;
- var serverPort = await _server.boundPort;
- authToken = await _server.authToken;
+ var serverHostname = await _server!.boundHostname;
+ var serverPort = await _server!.boundPort;
+ authToken = await _server!.authToken;
serverIsShutdown = Completer();
previewUrls = [
@@ -212,7 +212,7 @@
scheme: 'http',
host: serverHostname,
port: serverPort,
- path: state.pathMapper.map(includedRoot),
+ path: state.pathMapper!.map(includedRoot),
queryParameters: {'authToken': authToken}).toString()
];
}
@@ -310,7 +310,7 @@
bool packageConfigNeedsUpdate = false;
bool packageDepsUpdated = false;
var pubspecMap = pubspec.content;
- YamlNode environmentOptions;
+ YamlNode? environmentOptions;
if (pubspecMap is YamlMap) {
environmentOptions = pubspecMap.nodes['environment'];
}
@@ -334,7 +334,7 @@
packageConfigNeedsUpdate = true;
}
if (neededPackages.isNotEmpty) {
- YamlNode dependencies;
+ YamlNode? dependencies;
if (pubspecMap is YamlMap) {
dependencies = pubspecMap.nodes['dependencies'];
}
@@ -412,7 +412,7 @@
if (currentConstraint.min == null) {
_logger.stderr(invalidVersionMessage);
return false;
- } else if (currentConstraint.min >= minimumVersion) {
+ } else if (currentConstraint.min! >= minimumVersion) {
// The current version constraint is already up to date. Do not
// edit.
return false;
@@ -447,7 +447,7 @@
static void shutdownAllServers() {
for (var server in _allServers) {
try {
- server.close();
+ server!.close();
} catch (_) {}
}
_allServers.clear();
@@ -468,7 +468,7 @@
// do so if we are sure that stable release exists. An easy way to check
// that is to see if the current SDK version is greater than or equal to the
// stable release of null safety.
- var nullSafetyStableReleaseVersion = Feature.non_nullable.releaseVersion;
+ var nullSafetyStableReleaseVersion = Feature.non_nullable.releaseVersion!;
if (sdkVersion >= nullSafetyStableReleaseVersion) {
// It is, so we can use it as the minimum SDK constraint.
return nullSafetyStableReleaseVersion;
@@ -546,9 +546,9 @@
@override
void reportException(
- Source source, AstNode node, Object exception, StackTrace stackTrace) {
+ Source? source, AstNode? node, Object exception, StackTrace stackTrace) {
listener.client.onException('''
-$exception at offset ${node.offset} in $source ($node)
+$exception at offset ${node!.offset} in $source ($node)
$stackTrace''');
}
@@ -562,8 +562,8 @@
_YamlFile._(this.path, this.textContent, this.content);
- String _getName() {
- YamlNode packageNameNode;
+ String? _getName() {
+ YamlNode? packageNameNode;
if (content is YamlMap) {
packageNameNode = (content as YamlMap).nodes['name'];
@@ -572,7 +572,7 @@
}
if (packageNameNode is YamlScalar && packageNameNode.value is String) {
- return packageNameNode.value as String;
+ return packageNameNode.value as String?;
} else {
return null;
}
diff --git a/pkg/nnbd_migration/lib/src/front_end/offset_mapper.dart b/pkg/nnbd_migration/lib/src/front_end/offset_mapper.dart
index 0ff2d76..f051163 100644
--- a/pkg/nnbd_migration/lib/src/front_end/offset_mapper.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/offset_mapper.dart
@@ -41,7 +41,7 @@
/// Return the post-edit offset that corresponds to the given pre-edit
/// [offset], or `null` when that offset has been deleted.
- int map(int offset);
+ int? map(int? offset);
}
/// A mapper used for files that were modified by a set of edits.
@@ -61,14 +61,14 @@
}
@override
- int map(int offset) => offset + _deltaFor(offset);
+ int map(int? offset) => offset! + _deltaFor(offset);
/// Return the delta to be added to the pre-edit [offset] to produce the
/// post-edit offset.
- int _deltaFor(int offset) {
+ int _deltaFor(int? offset) {
for (var i = 0; i < _offsets.length; i++) {
var currentOffset = _offsets[i];
- if (currentOffset >= offset || currentOffset < 0) {
+ if (currentOffset >= offset! || currentOffset < 0) {
return _deltas[i];
}
}
@@ -95,7 +95,7 @@
/// A mapper used for files that were not modified.
class _IdentityMapper implements OffsetMapper {
@override
- int map(int offset) => offset;
+ int? map(int? offset) => offset;
}
class _OffsetMapperChain implements OffsetMapper {
@@ -104,7 +104,7 @@
_OffsetMapperChain(this.innerMappers);
@override
- int map(int offset) {
+ int? map(int? offset) {
for (final mapper in innerMappers) {
offset = mapper.map(offset);
if (offset == null) {
@@ -122,13 +122,13 @@
_RebasedOffsetMapper(this.rebaser, this.rebased);
@override
- int map(int offset) {
+ int? map(int? offset) {
final rebasedOffset = rebased.map(offset);
final rebasingOffset = rebaser.map(offset);
if (rebasedOffset == null || rebasingOffset == null) {
return null;
}
- final delta = rebasedOffset - offset;
+ final delta = rebasedOffset - offset!;
return rebasingOffset + delta;
}
}
@@ -147,8 +147,8 @@
this.offset, this.replacedLength, this.replacementLength);
@override
- int map(int offset) {
- if (offset < this.offset) {
+ int? map(int? offset) {
+ if (offset! < this.offset) {
return offset;
}
if (offset < this.offset + replacedLength) {
diff --git a/pkg/nnbd_migration/lib/src/front_end/region_renderer.dart b/pkg/nnbd_migration/lib/src/front_end/region_renderer.dart
index 33f4b6c..700a9e3 100644
--- a/pkg/nnbd_migration/lib/src/front_end/region_renderer.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/region_renderer.dart
@@ -18,11 +18,11 @@
/// The compilation unit information containing the region.
final UnitInfo unitInfo;
- final MigrationInfo migrationInfo;
+ final MigrationInfo? migrationInfo;
/// An object used to map the file paths of analyzed files to the file paths
/// of the HTML files used to view the content of those files.
- final PathMapper pathMapper;
+ final PathMapper? pathMapper;
/// The auth token for the current site, for use in generating URIs.
final String authToken;
@@ -33,12 +33,12 @@
this.pathMapper, this.authToken);
/// Returns the path context used to manipulate paths.
- path.Context get pathContext => migrationInfo.pathContext;
+ path.Context get pathContext => migrationInfo!.pathContext;
EditDetails render() {
TargetLink linkForTarget(NavigationTarget target) {
var relativePath =
- _relativePathToTarget(target, pathContext.dirname(unitInfo.path));
+ _relativePathToTarget(target, pathContext.dirname(unitInfo.path!));
var targetUri = _uriForPath(target.filePath, target);
return TargetLink(
path: relativePath,
@@ -49,7 +49,7 @@
EditLink linkForEdit(EditDetail edit) => EditLink(
description: edit.description,
- href: Uri(path: pathContext.basename(unitInfo.path), queryParameters: {
+ href: Uri(path: pathContext.basename(unitInfo.path!), queryParameters: {
'offset': edit.offset.toString(),
'end': (edit.offset + edit.length).toString(),
'replacement': edit.replacement
@@ -57,7 +57,7 @@
var response = EditDetails(
displayPath: unitInfo.path,
- uriPath: pathMapper.map(unitInfo.path),
+ uriPath: pathMapper!.map(unitInfo.path!),
line: region.lineNumber,
explanation: region.explanation,
edits: supportsIncrementalWorkflow
@@ -72,8 +72,9 @@
TraceEntry(
description: entry.description,
function: entry.function,
- link:
- entry.target == null ? null : linkForTarget(entry.target),
+ link: entry.target == null
+ ? null
+ : linkForTarget(entry.target!),
hintActions: entry.hintActions)
])
],
@@ -83,12 +84,8 @@
/// Returns the URL that will navigate to the given [target].
String _relativePathToTarget(NavigationTarget target, String unitDir) {
- if (target == null) {
- // TODO(brianwilkerson) This is temporary support until we can get targets
- // for all nodes.
- return '';
- }
- return pathMapper.map(pathContext.relative(target.filePath, from: unitDir));
+ return pathMapper!
+ .map(pathContext.relative(target.filePath, from: unitDir));
}
/// Return the URL that will navigate to the given [target] in the file at the
@@ -99,6 +96,6 @@
if (target.line != null) 'line': target.line,
'authToken': authToken,
}.entries.map((entry) => '${entry.key}=${entry.value}').join('&');
- return '${pathMapper.map(path)}?$queryParams';
+ return '${pathMapper!.map(path)}?$queryParams';
}
}
diff --git a/pkg/nnbd_migration/lib/src/front_end/resources/resources.g.dart b/pkg/nnbd_migration/lib/src/front_end/resources/resources.g.dart
index cedaf06..26f57f1 100644
--- a/pkg/nnbd_migration/lib/src/front_end/resources/resources.g.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/resources/resources.g.dart
@@ -2,10 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-// This file is explicitly opted in to null safety since it contains `String?`
-// types, even though the rest of the migration tool isn't migrated yet.
-// @dart=2.12
-
// This file is generated; don't edit it directly.
//
// See pkg/nnbd_migration/tool/codegen/generate_resources.dart for how
@@ -7712,7 +7708,7 @@
''';
String? _migration_js;
-// migration_dart md5 is '62f4a5cddf945de898f19cea860ec0b5'
+// migration_dart md5 is 'cbfeac5f79f12f2862fc0d4bc569f58a'
String _migration_js_base64 = '''
KGZ1bmN0aW9uIGRhcnRQcm9ncmFtKCl7ZnVuY3Rpb24gY29weVByb3BlcnRpZXMoYSxiKXt2YXIgcz1P
YmplY3Qua2V5cyhhKQpmb3IodmFyIHI9MDtyPHMubGVuZ3RoO3IrKyl7dmFyIHE9c1tyXQpiW3FdPWFb
@@ -7789,12 +7785,12 @@
QnlUYWcsc2V0T3JVcGRhdGVMZWFmVGFnczpzZXRPclVwZGF0ZUxlYWZUYWdzfX0oKQpmdW5jdGlvbiBp
bml0aWFsaXplRGVmZXJyZWRIdW5rKGEpe3g9di50eXBlcy5sZW5ndGgKYShodW5rSGVscGVycyx2LHcs
JCl9dmFyIEI9ewp3UjpmdW5jdGlvbigpe3JldHVybiBuZXcgQi5xcCgiIiwiIiwiIixDLkR4KX0sCllm
-OmZ1bmN0aW9uKGEpe3ZhciBzLHIscSxwLG8sbixtLGwsaz1ILmgoYS5xKDAsInJlZ2lvbnMiKSksaj1I
-LmgoYS5xKDAsIm5hdmlnYXRpb25Db250ZW50IikpLGk9SC5oKGEucSgwLCJzb3VyY2VDb2RlIikpLGg9
-UC5GbCh0LlgsdC5kXykKZm9yKHM9dC50LmEoYS5xKDAsImVkaXRzIikpLHM9cy5nUHUocykscz1zLmdt
-KHMpLHI9dC5VLHE9dC5oNDtzLkYoKTspe3A9cy5nbCgpCm89cC5hCm49SC5RSShbXSxxKQpmb3IocD1K
-LklUKHIuYShwLmIpKTtwLkYoKTspe209cC5nbCgpCmw9Si5VNihtKQpuLnB1c2gobmV3IEIuajgoSC51
-UChsLnEobSwibGluZSIpKSxILmgobC5xKG0sImV4cGxhbmF0aW9uIikpLEgudVAobC5xKG0sIm9mZnNl
+OmZ1bmN0aW9uKGEpe3ZhciBzLHIscSxwLG8sbixtLGwsaz1ILmsoYS5xKDAsInJlZ2lvbnMiKSksaj1I
+LmsoYS5xKDAsIm5hdmlnYXRpb25Db250ZW50IikpLGk9SC5rKGEucSgwLCJzb3VyY2VDb2RlIikpLGg9
+UC5GbCh0Lk4sdC5mNCkKZm9yKHM9dC5HLmEoYS5xKDAsImVkaXRzIikpLHM9cy5nUHUocykscz1zLmdt
+KHMpLHI9dC5SLHE9dC5naTtzLkYoKTspe3A9cy5nbCgpCm89cC5hCm49SC5RSShbXSxxKQpmb3IocD1K
+LklUKHIuYShwLmIpKTtwLkYoKTspe209cC5nbCgpCmw9Si5VNihtKQpuLnB1c2gobmV3IEIuajgoSC5V
+YyhsLnEobSwibGluZSIpKSxILmsobC5xKG0sImV4cGxhbmF0aW9uIikpLEguVWMobC5xKG0sIm9mZnNl
dCIpKSkpfWguWTUoMCxvLG4pfXJldHVybiBuZXcgQi5xcChrLGosaSxoKX0sCmo4OmZ1bmN0aW9uIGo4
KGEsYixjKXt0aGlzLmE9YQp0aGlzLmI9Ygp0aGlzLmM9Y30sCnFwOmZ1bmN0aW9uIHFwKGEsYixjLGQp
e3ZhciBfPXRoaXMKXy5hPWEKXy5iPWIKXy5jPWMKXy5kPWR9LApmdjpmdW5jdGlvbiBmdigpe30sCk9T
@@ -7806,518 +7802,497 @@
Zih0Lmc4LmIoSC5SdShzKSkpe3I9JC5GZgppZihyIT1udWxsKXJldHVybiByCnRocm93IHN9ZWxzZSB0
aHJvdyBzfWlmKEouUk0obywkLkk2KSl7cj0kLkZmCnIudG9TdHJpbmcKcmV0dXJuIHJ9JC5JNj1vCmlm
KCQuSGsoKT09JC5FYigpKXI9JC5GZj1vLlpJKCIuIikudygwKQplbHNle3E9by50NCgpCnA9cS5sZW5n
-dGgtMQpyPSQuRmY9cD09PTA/cTpDLnhCLk5qKHEsMCxwKX1yLnRvU3RyaW5nCnJldHVybiByfX0sRT17
-T0Y6ZnVuY3Rpb24gT0YoYSxiLGMpe3RoaXMuZD1hCnRoaXMuZT1iCnRoaXMuZj1jfX0sRj17cnU6ZnVu
-Y3Rpb24gcnUoYSxiLGMsZCl7dmFyIF89dGhpcwpfLmQ9YQpfLmU9YgpfLmY9YwpfLnI9ZH19LEg9e0ZL
-OmZ1bmN0aW9uIEZLKCl7fSwKR0o6ZnVuY3Rpb24oYSxiLGMpe2lmKGIuQygiYlE8MD4iKS5iKGEpKXJl
-dHVybiBuZXcgSC5vbChhLGIuQygiQDwwPiIpLktxKGMpLkMoIm9sPDEsMj4iKSkKcmV0dXJuIG5ldyBI
-Llp5KGEsYi5DKCJAPDA+IikuS3EoYykuQygiWnk8MSwyPiIpKX0sCmo6ZnVuY3Rpb24oYSl7cmV0dXJu
-IG5ldyBILm4oIkZpZWxkICciK0guRWooYSkrIicgaGFzIGJlZW4gYXNzaWduZWQgZHVyaW5nIGluaXRp
-YWxpemF0aW9uLiIpfSwKQmk6ZnVuY3Rpb24oYSl7cmV0dXJuIG5ldyBILnIzKGEpfSwKb286ZnVuY3Rp
-b24oYSl7dmFyIHMscj1hXjQ4CmlmKHI8PTkpcmV0dXJuIHIKcz1hfDMyCmlmKDk3PD1zJiZzPD0xMDIp
-cmV0dXJuIHMtODcKcmV0dXJuLTF9LApjYjpmdW5jdGlvbihhLGIsYyl7aWYoYT09bnVsbCl0aHJvdyBI
-LmIobmV3IEguR00oYixjLkMoIkdNPDA+IikpKQpyZXR1cm4gYX0sCnFDOmZ1bmN0aW9uKGEsYixjLGQp
-e1AuazEoYiwic3RhcnQiKQppZihjIT1udWxsKXtQLmsxKGMsImVuZCIpCmlmKGI+YylILnYoUC5URShi
-LDAsYywic3RhcnQiLG51bGwpKX1yZXR1cm4gbmV3IEgubkgoYSxiLGMsZC5DKCJuSDwwPiIpKX0sCksx
-OmZ1bmN0aW9uKGEsYixjLGQpe2lmKHQuZC5iKGEpKXJldHVybiBuZXcgSC54eShhLGIsYy5DKCJAPDA+
-IikuS3EoZCkuQygieHk8MSwyPiIpKQpyZXR1cm4gbmV3IEguaTEoYSxiLGMuQygiQDwwPiIpLktxKGQp
-LkMoImkxPDEsMj4iKSl9LApiSzpmdW5jdGlvbihhLGIsYyl7aWYodC5kLmIoYSkpe1AuazEoYiwiY291
-bnQiKQpyZXR1cm4gbmV3IEguZDUoYSxiLGMuQygiZDU8MD4iKSl9UC5rMShiLCJjb3VudCIpCnJldHVy
-biBuZXcgSC5BTShhLGIsYy5DKCJBTTwwPiIpKX0sCldwOmZ1bmN0aW9uKCl7cmV0dXJuIG5ldyBQLmxq
-KCJObyBlbGVtZW50Iil9LApBbTpmdW5jdGlvbigpe3JldHVybiBuZXcgUC5saigiVG9vIG1hbnkgZWxl
-bWVudHMiKX0sCmFyOmZ1bmN0aW9uKCl7cmV0dXJuIG5ldyBQLmxqKCJUb28gZmV3IGVsZW1lbnRzIil9
-LApCUjpmdW5jdGlvbiBCUigpe30sCkU3OmZ1bmN0aW9uIEU3KGEsYil7dGhpcy5hPWEKdGhpcy4kdGk9
-Yn0sClp5OmZ1bmN0aW9uIFp5KGEsYil7dGhpcy5hPWEKdGhpcy4kdGk9Yn0sCm9sOmZ1bmN0aW9uIG9s
-KGEsYil7dGhpcy5hPWEKdGhpcy4kdGk9Yn0sClVxOmZ1bmN0aW9uIFVxKCl7fSwKalY6ZnVuY3Rpb24g
-alYoYSxiKXt0aGlzLmE9YQp0aGlzLiR0aT1ifSwKbjpmdW5jdGlvbiBuKGEpe3RoaXMuYT1hfSwKcjM6
-ZnVuY3Rpb24gcjMoYSl7dGhpcy5hPWF9LApxajpmdW5jdGlvbiBxaihhKXt0aGlzLmE9YX0sCkdNOmZ1
-bmN0aW9uIEdNKGEsYil7dGhpcy5hPWEKdGhpcy4kdGk9Yn0sCmJROmZ1bmN0aW9uIGJRKCl7fSwKYUw6
-ZnVuY3Rpb24gYUwoKXt9LApuSDpmdW5jdGlvbiBuSChhLGIsYyxkKXt2YXIgXz10aGlzCl8uYT1hCl8u
-Yj1iCl8uYz1jCl8uJHRpPWR9LAphNzpmdW5jdGlvbiBhNyhhLGIsYyl7dmFyIF89dGhpcwpfLmE9YQpf
-LmI9YgpfLmM9MApfLmQ9bnVsbApfLiR0aT1jfSwKaTE6ZnVuY3Rpb24gaTEoYSxiLGMpe3RoaXMuYT1h
-CnRoaXMuYj1iCnRoaXMuJHRpPWN9LAp4eTpmdW5jdGlvbiB4eShhLGIsYyl7dGhpcy5hPWEKdGhpcy5i
-PWIKdGhpcy4kdGk9Y30sCk1IOmZ1bmN0aW9uIE1IKGEsYixjKXt2YXIgXz10aGlzCl8uYT1udWxsCl8u
-Yj1hCl8uYz1iCl8uJHRpPWN9LApsSjpmdW5jdGlvbiBsSihhLGIsYyl7dGhpcy5hPWEKdGhpcy5iPWIK
-dGhpcy4kdGk9Y30sClU1OmZ1bmN0aW9uIFU1KGEsYixjKXt0aGlzLmE9YQp0aGlzLmI9Ygp0aGlzLiR0
-aT1jfSwKU086ZnVuY3Rpb24gU08oYSxiLGMpe3RoaXMuYT1hCnRoaXMuYj1iCnRoaXMuJHRpPWN9LApB
-TTpmdW5jdGlvbiBBTShhLGIsYyl7dGhpcy5hPWEKdGhpcy5iPWIKdGhpcy4kdGk9Y30sCmQ1OmZ1bmN0
-aW9uIGQ1KGEsYixjKXt0aGlzLmE9YQp0aGlzLmI9Ygp0aGlzLiR0aT1jfSwKVTE6ZnVuY3Rpb24gVTEo
-YSxiLGMpe3RoaXMuYT1hCnRoaXMuYj1iCnRoaXMuJHRpPWN9LApNQjpmdW5jdGlvbiBNQihhKXt0aGlz
-LiR0aT1hfSwKRnU6ZnVuY3Rpb24gRnUoYSl7dGhpcy4kdGk9YX0sCnU2OmZ1bmN0aW9uIHU2KGEsYil7
-dGhpcy5hPWEKdGhpcy4kdGk9Yn0sCkpCOmZ1bmN0aW9uIEpCKGEsYil7dGhpcy5hPWEKdGhpcy4kdGk9
-Yn0sClNVOmZ1bmN0aW9uIFNVKCl7fSwKUmU6ZnVuY3Rpb24gUmUoKXt9LAp3MjpmdW5jdGlvbiB3Migp
-e30sCnd2OmZ1bmN0aW9uIHd2KGEpe3RoaXMuYT1hfSwKUUM6ZnVuY3Rpb24gUUMoKXt9LApkYzpmdW5j
-dGlvbigpe3Rocm93IEguYihQLkw0KCJDYW5ub3QgbW9kaWZ5IHVubW9kaWZpYWJsZSBNYXAiKSl9LApw
-OmZ1bmN0aW9uKGEpe3ZhciBzLHI9di5tYW5nbGVkR2xvYmFsTmFtZXNbYV0KaWYociE9bnVsbClyZXR1
-cm4gcgpzPSJtaW5pZmllZDoiK2EKcmV0dXJuIHN9LAp3VjpmdW5jdGlvbihhLGIpe3ZhciBzCmlmKGIh
-PW51bGwpe3M9Yi54CmlmKHMhPW51bGwpcmV0dXJuIHN9cmV0dXJuIHQuYVUuYihhKX0sCkVqOmZ1bmN0
-aW9uKGEpe3ZhciBzCmlmKHR5cGVvZiBhPT0ic3RyaW5nIilyZXR1cm4gYQppZih0eXBlb2YgYT09Im51
-bWJlciIpe2lmKGEhPT0wKXJldHVybiIiK2F9ZWxzZSBpZighMD09PWEpcmV0dXJuInRydWUiCmVsc2Ug
-aWYoITE9PT1hKXJldHVybiJmYWxzZSIKZWxzZSBpZihhPT1udWxsKXJldHVybiJudWxsIgpzPUoudyhh
-KQppZih0eXBlb2YgcyE9InN0cmluZyIpdGhyb3cgSC5iKEgudEwoYSkpCnJldHVybiBzfSwKZVE6ZnVu
-Y3Rpb24oYSl7dmFyIHM9YS4kaWRlbnRpdHlIYXNoCmlmKHM9PW51bGwpe3M9TWF0aC5yYW5kb20oKSow
-eDNmZmZmZmZmfDAKYS4kaWRlbnRpdHlIYXNoPXN9cmV0dXJuIHN9LApIcDpmdW5jdGlvbihhLGIpe3Zh
-ciBzLHIscSxwLG8sbixtPW51bGwKaWYodHlwZW9mIGEhPSJzdHJpbmciKUgudihILnRMKGEpKQpzPS9e
-XHMqWystXT8oKDB4W2EtZjAtOV0rKXwoXGQrKXwoW2EtejAtOV0rKSlccyokL2kuZXhlYyhhKQppZihz
-PT1udWxsKXJldHVybiBtCmlmKDM+PXMubGVuZ3RoKXJldHVybiBILk9IKHMsMykKcj1zWzNdCmlmKGI9
-PW51bGwpe2lmKHIhPW51bGwpcmV0dXJuIHBhcnNlSW50KGEsMTApCmlmKHNbMl0hPW51bGwpcmV0dXJu
-IHBhcnNlSW50KGEsMTYpCnJldHVybiBtfWlmKGI8Mnx8Yj4zNil0aHJvdyBILmIoUC5URShiLDIsMzYs
-InJhZGl4IixtKSkKaWYoYj09PTEwJiZyIT1udWxsKXJldHVybiBwYXJzZUludChhLDEwKQppZihiPDEw
-fHxyPT1udWxsKXtxPWI8PTEwPzQ3K2I6ODYrYgpwPXNbMV0KZm9yKG89cC5sZW5ndGgsbj0wO248bzsr
-K24paWYoKEMueEIuVyhwLG4pfDMyKT5xKXJldHVybiBtfXJldHVybiBwYXJzZUludChhLGIpfSwKbGg6
-ZnVuY3Rpb24oYSl7cmV0dXJuIEguSDUoYSl9LApINTpmdW5jdGlvbihhKXt2YXIgcyxyLHEscAppZihh
-IGluc3RhbmNlb2YgUC5NaClyZXR1cm4gSC5kbShILnpLKGEpLG51bGwpCmlmKEouaWEoYSk9PT1DLk9r
-fHx0LmJKLmIoYSkpe3M9Qy5PNChhKQpyPXMhPT0iT2JqZWN0IiYmcyE9PSIiCmlmKHIpcmV0dXJuIHMK
-cT1hLmNvbnN0cnVjdG9yCmlmKHR5cGVvZiBxPT0iZnVuY3Rpb24iKXtwPXEubmFtZQppZih0eXBlb2Yg
-cD09InN0cmluZyIpcj1wIT09Ik9iamVjdCImJnAhPT0iIgplbHNlIHI9ITEKaWYocilyZXR1cm4gcH19
-cmV0dXJuIEguZG0oSC56SyhhKSxudWxsKX0sCk0wOmZ1bmN0aW9uKCl7aWYoISFzZWxmLmxvY2F0aW9u
-KXJldHVybiBzZWxmLmxvY2F0aW9uLmhyZWYKcmV0dXJuIG51bGx9LApWSzpmdW5jdGlvbihhKXt2YXIg
-cyxyLHEscCxvPWEubGVuZ3RoCmlmKG88PTUwMClyZXR1cm4gU3RyaW5nLmZyb21DaGFyQ29kZS5hcHBs
-eShudWxsLGEpCmZvcihzPSIiLHI9MDtyPG87cj1xKXtxPXIrNTAwCnA9cTxvP3E6bwpzKz1TdHJpbmcu
-ZnJvbUNoYXJDb2RlLmFwcGx5KG51bGwsYS5zbGljZShyLHApKX1yZXR1cm4gc30sCkNxOmZ1bmN0aW9u
-KGEpe3ZhciBzLHIscSxwPUguUUkoW10sdC5hKQpmb3Iocz1hLmxlbmd0aCxyPTA7cjxhLmxlbmd0aDth
-Lmxlbmd0aD09PXN8fCgwLEgubGspKGEpLCsrcil7cT1hW3JdCmlmKCFILm9rKHEpKXRocm93IEguYihI
-LnRMKHEpKQppZihxPD02NTUzNSlDLk5tLmkocCxxKQplbHNlIGlmKHE8PTExMTQxMTEpe0MuTm0uaShw
-LDU1Mjk2KyhDLmpuLndHKHEtNjU1MzYsMTApJjEwMjMpKQpDLk5tLmkocCw1NjMyMCsocSYxMDIzKSl9
-ZWxzZSB0aHJvdyBILmIoSC50TChxKSl9cmV0dXJuIEguVksocCl9LAplVDpmdW5jdGlvbihhKXt2YXIg
-cyxyLHEKZm9yKHM9YS5sZW5ndGgscj0wO3I8czsrK3Ipe3E9YVtyXQppZighSC5vayhxKSl0aHJvdyBI
-LmIoSC50TChxKSkKaWYocTwwKXRocm93IEguYihILnRMKHEpKQppZihxPjY1NTM1KXJldHVybiBILkNx
-KGEpfXJldHVybiBILlZLKGEpfSwKZnc6ZnVuY3Rpb24oYSxiLGMpe3ZhciBzLHIscSxwCmlmKGM8PTUw
-MCYmYj09PTAmJmM9PT1hLmxlbmd0aClyZXR1cm4gU3RyaW5nLmZyb21DaGFyQ29kZS5hcHBseShudWxs
-LGEpCmZvcihzPWIscj0iIjtzPGM7cz1xKXtxPXMrNTAwCnA9cTxjP3E6YwpyKz1TdHJpbmcuZnJvbUNo
-YXJDb2RlLmFwcGx5KG51bGwsYS5zdWJhcnJheShzLHApKX1yZXR1cm4gcn0sCkx3OmZ1bmN0aW9uKGEp
-e3ZhciBzCmlmKDA8PWEpe2lmKGE8PTY1NTM1KXJldHVybiBTdHJpbmcuZnJvbUNoYXJDb2RlKGEpCmlm
-KGE8PTExMTQxMTEpe3M9YS02NTUzNgpyZXR1cm4gU3RyaW5nLmZyb21DaGFyQ29kZSgoQy5qbi53Ryhz
-LDEwKXw1NTI5Nik+Pj4wLHMmMTAyM3w1NjMyMCl9fXRocm93IEguYihQLlRFKGEsMCwxMTE0MTExLG51
-bGwsbnVsbCkpfSwKbzI6ZnVuY3Rpb24oYSl7aWYoYS5kYXRlPT09dm9pZCAwKWEuZGF0ZT1uZXcgRGF0
-ZShhLmEpCnJldHVybiBhLmRhdGV9LAp0SjpmdW5jdGlvbihhKXt2YXIgcz1ILm8yKGEpLmdldEZ1bGxZ
-ZWFyKCkrMApyZXR1cm4gc30sCk5TOmZ1bmN0aW9uKGEpe3ZhciBzPUgubzIoYSkuZ2V0TW9udGgoKSsx
-CnJldHVybiBzfSwKakE6ZnVuY3Rpb24oYSl7dmFyIHM9SC5vMihhKS5nZXREYXRlKCkrMApyZXR1cm4g
-c30sCklYOmZ1bmN0aW9uKGEpe3ZhciBzPUgubzIoYSkuZ2V0SG91cnMoKSswCnJldHVybiBzfSwKY2g6
-ZnVuY3Rpb24oYSl7dmFyIHM9SC5vMihhKS5nZXRNaW51dGVzKCkrMApyZXR1cm4gc30sCkpkOmZ1bmN0
-aW9uKGEpe3ZhciBzPUgubzIoYSkuZ2V0U2Vjb25kcygpKzAKcmV0dXJuIHN9LApvMTpmdW5jdGlvbihh
-KXt2YXIgcz1ILm8yKGEpLmdldE1pbGxpc2Vjb25kcygpKzAKcmV0dXJuIHN9LAp6bzpmdW5jdGlvbihh
-LGIsYyl7dmFyIHMscixxPXt9CnEuYT0wCnM9W10Kcj1bXQpxLmE9Yi5sZW5ndGgKQy5ObS5GVihzLGIp
-CnEuYj0iIgppZihjIT1udWxsJiZjLmEhPT0wKWMuSygwLG5ldyBILkNqKHEscixzKSkKIiIrcS5hCnJl
-dHVybiBKLkp5KGEsbmV3IEguTEkoQy5UZSwwLHMsciwwKSl9LApFazpmdW5jdGlvbihhLGIsYyl7dmFy
-IHMscixxLHAKaWYoYiBpbnN0YW5jZW9mIEFycmF5KXM9Yz09bnVsbHx8Yy5hPT09MAplbHNlIHM9ITEK
-aWYocyl7cj1iCnE9ci5sZW5ndGgKaWYocT09PTApe2lmKCEhYS4kMClyZXR1cm4gYS4kMCgpfWVsc2Ug
-aWYocT09PTEpe2lmKCEhYS4kMSlyZXR1cm4gYS4kMShyWzBdKX1lbHNlIGlmKHE9PT0yKXtpZighIWEu
-JDIpcmV0dXJuIGEuJDIoclswXSxyWzFdKX1lbHNlIGlmKHE9PT0zKXtpZighIWEuJDMpcmV0dXJuIGEu
-JDMoclswXSxyWzFdLHJbMl0pfWVsc2UgaWYocT09PTQpe2lmKCEhYS4kNClyZXR1cm4gYS4kNChyWzBd
-LHJbMV0sclsyXSxyWzNdKX1lbHNlIGlmKHE9PT01KWlmKCEhYS4kNSlyZXR1cm4gYS4kNShyWzBdLHJb
-MV0sclsyXSxyWzNdLHJbNF0pCnA9YVsiIisiJCIrcV0KaWYocCE9bnVsbClyZXR1cm4gcC5hcHBseShh
-LHIpfXJldHVybiBILmUxKGEsYixjKX0sCmUxOmZ1bmN0aW9uKGEsYixjKXt2YXIgcyxyLHEscCxvLG4s
-bSxsLGssaixpPWIgaW5zdGFuY2VvZiBBcnJheT9iOlAuQ0goYiwhMCx0LnopLGg9aS5sZW5ndGgsZz1h
-LiRSCmlmKGg8ZylyZXR1cm4gSC56byhhLGksYykKcz1hLiRECnI9cz09bnVsbApxPSFyP3MoKTpudWxs
-CnA9Si5pYShhKQpvPXAuJEMKaWYodHlwZW9mIG89PSJzdHJpbmciKW89cFtvXQppZihyKXtpZihjIT1u
-dWxsJiZjLmEhPT0wKXJldHVybiBILnpvKGEsaSxjKQppZihoPT09ZylyZXR1cm4gby5hcHBseShhLGkp
-CnJldHVybiBILnpvKGEsaSxjKX1pZihxIGluc3RhbmNlb2YgQXJyYXkpe2lmKGMhPW51bGwmJmMuYSE9
-PTApcmV0dXJuIEguem8oYSxpLGMpCmlmKGg+ZytxLmxlbmd0aClyZXR1cm4gSC56byhhLGksbnVsbCkK
-Qy5ObS5GVihpLHEuc2xpY2UoaC1nKSkKcmV0dXJuIG8uYXBwbHkoYSxpKX1lbHNle2lmKGg+ZylyZXR1
-cm4gSC56byhhLGksYykKbj1PYmplY3Qua2V5cyhxKQppZihjPT1udWxsKWZvcihyPW4ubGVuZ3RoLG09
-MDttPG4ubGVuZ3RoO24ubGVuZ3RoPT09cnx8KDAsSC5saykobiksKyttKXtsPXFbSC5oKG5bbV0pXQpp
-ZihDLk52PT09bClyZXR1cm4gSC56byhhLGksYykKQy5ObS5pKGksbCl9ZWxzZXtmb3Iocj1uLmxlbmd0
-aCxrPTAsbT0wO208bi5sZW5ndGg7bi5sZW5ndGg9PT1yfHwoMCxILmxrKShuKSwrK20pe2o9SC5oKG5b
-bV0pCmlmKGMueDQoaikpeysrawpDLk5tLmkoaSxjLnEoMCxqKSl9ZWxzZXtsPXFbal0KaWYoQy5Odj09
-PWwpcmV0dXJuIEguem8oYSxpLGMpCkMuTm0uaShpLGwpfX1pZihrIT09Yy5hKXJldHVybiBILnpvKGEs
-aSxjKX1yZXR1cm4gby5hcHBseShhLGkpfX0sCnBZOmZ1bmN0aW9uKGEpe3Rocm93IEguYihILnRMKGEp
-KX0sCk9IOmZ1bmN0aW9uKGEsYil7aWYoYT09bnVsbClKLkhtKGEpCnRocm93IEguYihILnUoYSxiKSl9
-LAp1OmZ1bmN0aW9uKGEsYil7dmFyIHMscixxPSJpbmRleCIKaWYoIUgub2soYikpcmV0dXJuIG5ldyBQ
-LmMoITAsYixxLG51bGwpCnM9SC51UChKLkhtKGEpKQppZighKGI8MCkpe2lmKHR5cGVvZiBzIT09Im51
-bWJlciIpcmV0dXJuIEgucFkocykKcj1iPj1zfWVsc2Ugcj0hMAppZihyKXJldHVybiBQLkNmKGIsYSxx
-LG51bGwscykKcmV0dXJuIFAuTzcoYixxKX0sCkR1OmZ1bmN0aW9uKGEsYixjKXtpZihhPmMpcmV0dXJu
-IFAuVEUoYSwwLGMsInN0YXJ0IixudWxsKQppZihiIT1udWxsKWlmKGI8YXx8Yj5jKXJldHVybiBQLlRF
-KGIsYSxjLCJlbmQiLG51bGwpCnJldHVybiBuZXcgUC5jKCEwLGIsImVuZCIsbnVsbCl9LAp0TDpmdW5j
-dGlvbihhKXtyZXR1cm4gbmV3IFAuYyghMCxhLG51bGwsbnVsbCl9LApiOmZ1bmN0aW9uKGEpe3ZhciBz
-LHIKaWYoYT09bnVsbClhPW5ldyBQLkxLKCkKcz1uZXcgRXJyb3IoKQpzLmRhcnRFeGNlcHRpb249YQpy
-PUguSnUKaWYoImRlZmluZVByb3BlcnR5IiBpbiBPYmplY3Qpe09iamVjdC5kZWZpbmVQcm9wZXJ0eShz
-LCJtZXNzYWdlIix7Z2V0OnJ9KQpzLm5hbWU9IiJ9ZWxzZSBzLnRvU3RyaW5nPXIKcmV0dXJuIHN9LApK
-dTpmdW5jdGlvbigpe3JldHVybiBKLncodGhpcy5kYXJ0RXhjZXB0aW9uKX0sCnY6ZnVuY3Rpb24oYSl7
-dGhyb3cgSC5iKGEpfSwKbGs6ZnVuY3Rpb24oYSl7dGhyb3cgSC5iKFAuYTQoYSkpfSwKY006ZnVuY3Rp
-b24oYSl7dmFyIHMscixxLHAsbyxuCmE9SC5lQShhLnJlcGxhY2UoU3RyaW5nKHt9KSwiJHJlY2VpdmVy
-JCIpKQpzPWEubWF0Y2goL1xcXCRbYS16QS1aXStcXFwkL2cpCmlmKHM9PW51bGwpcz1ILlFJKFtdLHQu
-cykKcj1zLmluZGV4T2YoIlxcJGFyZ3VtZW50c1xcJCIpCnE9cy5pbmRleE9mKCJcXCRhcmd1bWVudHNF
-eHByXFwkIikKcD1zLmluZGV4T2YoIlxcJGV4cHJcXCQiKQpvPXMuaW5kZXhPZigiXFwkbWV0aG9kXFwk
-IikKbj1zLmluZGV4T2YoIlxcJHJlY2VpdmVyXFwkIikKcmV0dXJuIG5ldyBILmY5KGEucmVwbGFjZShu
-ZXcgUmVnRXhwKCJcXFxcXFwkYXJndW1lbnRzXFxcXFxcJCIsImciKSwiKCg/Onh8W154XSkqKSIpLnJl
-cGxhY2UobmV3IFJlZ0V4cCgiXFxcXFxcJGFyZ3VtZW50c0V4cHJcXFxcXFwkIiwiZyIpLCIoKD86eHxb
-XnhdKSopIikucmVwbGFjZShuZXcgUmVnRXhwKCJcXFxcXFwkZXhwclxcXFxcXCQiLCJnIiksIigoPzp4
-fFteeF0pKikiKS5yZXBsYWNlKG5ldyBSZWdFeHAoIlxcXFxcXCRtZXRob2RcXFxcXFwkIiwiZyIpLCIo
-KD86eHxbXnhdKSopIikucmVwbGFjZShuZXcgUmVnRXhwKCJcXFxcXFwkcmVjZWl2ZXJcXFxcXFwkIiwi
-ZyIpLCIoKD86eHxbXnhdKSopIikscixxLHAsbyxuKX0sClM3OmZ1bmN0aW9uKGEpe3JldHVybiBmdW5j
-dGlvbigkZXhwciQpe3ZhciAkYXJndW1lbnRzRXhwciQ9IiRhcmd1bWVudHMkIgp0cnl7JGV4cHIkLiRt
-ZXRob2QkKCRhcmd1bWVudHNFeHByJCl9Y2F0Y2gocyl7cmV0dXJuIHMubWVzc2FnZX19KGEpfSwKTWo6
-ZnVuY3Rpb24oYSl7cmV0dXJuIGZ1bmN0aW9uKCRleHByJCl7dHJ5eyRleHByJC4kbWV0aG9kJH1jYXRj
-aChzKXtyZXR1cm4gcy5tZXNzYWdlfX0oYSl9LApUMzpmdW5jdGlvbihhLGIpe3ZhciBzPWI9PW51bGws
-cj1zP251bGw6Yi5tZXRob2QKcmV0dXJuIG5ldyBILmF6KGEscixzP251bGw6Yi5yZWNlaXZlcil9LApS
-dTpmdW5jdGlvbihhKXtpZihhPT1udWxsKXJldHVybiBuZXcgSC50ZShhKQppZihhIGluc3RhbmNlb2Yg
-SC5icSlyZXR1cm4gSC50VyhhLGEuYSkKaWYodHlwZW9mIGEhPT0ib2JqZWN0IilyZXR1cm4gYQppZigi
-ZGFydEV4Y2VwdGlvbiIgaW4gYSlyZXR1cm4gSC50VyhhLGEuZGFydEV4Y2VwdGlvbikKcmV0dXJuIEgu
-dGwoYSl9LAp0VzpmdW5jdGlvbihhLGIpe2lmKHQuci5iKGIpKWlmKGIuJHRocm93bkpzRXJyb3I9PW51
-bGwpYi4kdGhyb3duSnNFcnJvcj1hCnJldHVybiBifSwKdGw6ZnVuY3Rpb24oYSl7dmFyIHMscixxLHAs
-byxuLG0sbCxrLGosaSxoLGcsZixlPW51bGwKaWYoISgibWVzc2FnZSIgaW4gYSkpcmV0dXJuIGEKcz1h
-Lm1lc3NhZ2UKaWYoIm51bWJlciIgaW4gYSYmdHlwZW9mIGEubnVtYmVyPT0ibnVtYmVyIil7cj1hLm51
-bWJlcgpxPXImNjU1MzUKaWYoKEMuam4ud0cociwxNikmODE5MSk9PT0xMClzd2l0Y2gocSl7Y2FzZSA0
-Mzg6cmV0dXJuIEgudFcoYSxILlQzKEguRWoocykrIiAoRXJyb3IgIitxKyIpIixlKSkKY2FzZSA0NDU6
-Y2FzZSA1MDA3OnA9SC5FaihzKSsiIChFcnJvciAiK3ErIikiCnJldHVybiBILnRXKGEsbmV3IEguVzAo
-cCxlKSl9fWlmKGEgaW5zdGFuY2VvZiBUeXBlRXJyb3Ipe289JC5TbigpCm49JC5scSgpCm09JC5OOSgp
-Cmw9JC5pSSgpCms9JC5VTigpCmo9JC5aaCgpCmk9JC5yTigpCiQuYzMoKQpoPSQuSEsoKQpnPSQucjEo
-KQpmPW8ucVMocykKaWYoZiE9bnVsbClyZXR1cm4gSC50VyhhLEguVDMoSC5oKHMpLGYpKQplbHNle2Y9
-bi5xUyhzKQppZihmIT1udWxsKXtmLm1ldGhvZD0iY2FsbCIKcmV0dXJuIEgudFcoYSxILlQzKEguaChz
-KSxmKSl9ZWxzZXtmPW0ucVMocykKaWYoZj09bnVsbCl7Zj1sLnFTKHMpCmlmKGY9PW51bGwpe2Y9ay5x
-UyhzKQppZihmPT1udWxsKXtmPWoucVMocykKaWYoZj09bnVsbCl7Zj1pLnFTKHMpCmlmKGY9PW51bGwp
-e2Y9bC5xUyhzKQppZihmPT1udWxsKXtmPWgucVMocykKaWYoZj09bnVsbCl7Zj1nLnFTKHMpCnA9ZiE9
-bnVsbH1lbHNlIHA9ITB9ZWxzZSBwPSEwfWVsc2UgcD0hMH1lbHNlIHA9ITB9ZWxzZSBwPSEwfWVsc2Ug
-cD0hMH1lbHNlIHA9ITAKaWYocCl7SC5oKHMpCnJldHVybiBILnRXKGEsbmV3IEguVzAocyxmPT1udWxs
-P2U6Zi5tZXRob2QpKX19fXJldHVybiBILnRXKGEsbmV3IEgudlYodHlwZW9mIHM9PSJzdHJpbmciP3M6
-IiIpKX1pZihhIGluc3RhbmNlb2YgUmFuZ2VFcnJvcil7aWYodHlwZW9mIHM9PSJzdHJpbmciJiZzLmlu
-ZGV4T2YoImNhbGwgc3RhY2siKSE9PS0xKXJldHVybiBuZXcgUC5LWSgpCnM9ZnVuY3Rpb24oYil7dHJ5
-e3JldHVybiBTdHJpbmcoYil9Y2F0Y2goZCl7fXJldHVybiBudWxsfShhKQpyZXR1cm4gSC50VyhhLG5l
-dyBQLmMoITEsZSxlLHR5cGVvZiBzPT0ic3RyaW5nIj9zLnJlcGxhY2UoL15SYW5nZUVycm9yOlxzKi8s
-IiIpOnMpKX1pZih0eXBlb2YgSW50ZXJuYWxFcnJvcj09ImZ1bmN0aW9uIiYmYSBpbnN0YW5jZW9mIElu
-dGVybmFsRXJyb3IpaWYodHlwZW9mIHM9PSJzdHJpbmciJiZzPT09InRvbyBtdWNoIHJlY3Vyc2lvbiIp
-cmV0dXJuIG5ldyBQLktZKCkKcmV0dXJuIGF9LAp0czpmdW5jdGlvbihhKXt2YXIgcwppZihhIGluc3Rh
-bmNlb2YgSC5icSlyZXR1cm4gYS5iCmlmKGE9PW51bGwpcmV0dXJuIG5ldyBILlhPKGEpCnM9YS4kY2Fj
-aGVkVHJhY2UKaWYocyE9bnVsbClyZXR1cm4gcwpyZXR1cm4gYS4kY2FjaGVkVHJhY2U9bmV3IEguWE8o
-YSl9LApCNzpmdW5jdGlvbihhLGIpe3ZhciBzLHIscSxwPWEubGVuZ3RoCmZvcihzPTA7czxwO3M9cSl7
-cj1zKzEKcT1yKzEKYi5ZNSgwLGFbc10sYVtyXSl9cmV0dXJuIGJ9LApmdDpmdW5jdGlvbihhLGIsYyxk
-LGUsZil7dC5ZLmEoYSkKc3dpdGNoKEgudVAoYikpe2Nhc2UgMDpyZXR1cm4gYS4kMCgpCmNhc2UgMTpy
-ZXR1cm4gYS4kMShjKQpjYXNlIDI6cmV0dXJuIGEuJDIoYyxkKQpjYXNlIDM6cmV0dXJuIGEuJDMoYyxk
-LGUpCmNhc2UgNDpyZXR1cm4gYS4kNChjLGQsZSxmKX10aHJvdyBILmIobmV3IFAuQ0QoIlVuc3VwcG9y
-dGVkIG51bWJlciBvZiBhcmd1bWVudHMgZm9yIHdyYXBwZWQgY2xvc3VyZSIpKX0sCnRSOmZ1bmN0aW9u
-KGEsYil7dmFyIHMKaWYoYT09bnVsbClyZXR1cm4gbnVsbApzPWEuJGlkZW50aXR5CmlmKCEhcylyZXR1
-cm4gcwpzPWZ1bmN0aW9uKGMsZCxlKXtyZXR1cm4gZnVuY3Rpb24oZixnLGgsaSl7cmV0dXJuIGUoYyxk
-LGYsZyxoLGkpfX0oYSxiLEguZnQpCmEuJGlkZW50aXR5PXMKcmV0dXJuIHN9LAppQTpmdW5jdGlvbihh
-Mil7dmFyIHMscixxLHAsbyxuLG0sbCxrLGosaT1hMi5jbyxoPWEyLmlTLGc9YTIuaUksZj1hMi5uREEs
-ZT1hMi5hSSxkPWEyLmZzLGM9YTIuY3MsYj1kWzBdLGE9Y1swXSxhMD1pW2JdLGExPWEyLmZUCmExLnRv
-U3RyaW5nCkgub1QoaCkKcz1oP09iamVjdC5jcmVhdGUobmV3IEguengoKS5jb25zdHJ1Y3Rvci5wcm90
-b3R5cGUpOk9iamVjdC5jcmVhdGUobmV3IEguclQobnVsbCxudWxsKS5jb25zdHJ1Y3Rvci5wcm90b3R5
-cGUpCnMuJGluaXRpYWxpemU9cy5jb25zdHJ1Y3RvcgppZihoKXI9ZnVuY3Rpb24gc3RhdGljX3RlYXJf
-b2ZmKCl7dGhpcy4kaW5pdGlhbGl6ZSgpfQplbHNle3E9JC55agppZih0eXBlb2YgcSE9PSJudW1iZXIi
-KXJldHVybiBxLmgoKQokLnlqPXErMQpxPW5ldyBGdW5jdGlvbigiYSxiIitxLCJ0aGlzLiRpbml0aWFs
-aXplKGEsYiIrcSsiKSIpCnI9cX1zLmNvbnN0cnVjdG9yPXIKci5wcm90b3R5cGU9cwpzLiRfbmFtZT1i
-CnMuJF90YXJnZXQ9YTAKcT0haAppZihxKXA9SC5ieChiLGEwLGcsZikKZWxzZXtzLiRzdGF0aWNfbmFt
-ZT1iCnA9YTB9cy4kUz1ILmltKGExLGgsZykKc1thXT1wCmZvcihvPXAsbj0xO248ZC5sZW5ndGg7Kytu
-KXttPWRbbl0KaWYodHlwZW9mIG09PSJzdHJpbmciKXtsPWlbbV0Kaz1tCm09bH1lbHNlIGs9IiIKaj1j
-W25dCmlmKGohPW51bGwpe2lmKHEpbT1ILmJ4KGssbSxnLGYpCnNbal09bX1pZihuPT09ZSlvPW19cy4k
-Qz1vCnMuJFI9YTIuckMKcy4kRD1hMi5kVgpyZXR1cm4gcn0sCmltOmZ1bmN0aW9uKGEsYixjKXt2YXIg
-cwppZih0eXBlb2YgYT09Im51bWJlciIpcmV0dXJuIGZ1bmN0aW9uKGQsZSl7cmV0dXJuIGZ1bmN0aW9u
-KCl7cmV0dXJuIGQoZSl9fShILkJwLGEpCmlmKHR5cGVvZiBhPT0ic3RyaW5nIil7aWYoSC5vVChiKSl0
-aHJvdyBILmIoIkNhbm5vdCBjb21wdXRlIHNpZ25hdHVyZSBmb3Igc3RhdGljIHRlYXJvZmYuIikKcz1I
-Lm9UKGMpP0guUFc6SC5UbgpyZXR1cm4gZnVuY3Rpb24oZCxlKXtyZXR1cm4gZnVuY3Rpb24oKXtyZXR1
+dGgtMQpyPSQuRmY9cD09PTA/cTpDLnhCLk5qKHEsMCxwKX1yZXR1cm4gcn19LEU9e09GOmZ1bmN0aW9u
+IE9GKGEsYixjKXt0aGlzLmQ9YQp0aGlzLmU9Ygp0aGlzLmY9Y319LEY9e3J1OmZ1bmN0aW9uIHJ1KGEs
+YixjLGQpe3ZhciBfPXRoaXMKXy5kPWEKXy5lPWIKXy5mPWMKXy5yPWR9fSxIPXtGSzpmdW5jdGlvbiBG
+Sygpe30sCkdKOmZ1bmN0aW9uKGEsYixjKXtpZihiLkMoImJRPDA+IikuYihhKSlyZXR1cm4gbmV3IEgu
+b2woYSxiLkMoIkA8MD4iKS5LcShjKS5DKCJvbDwxLDI+IikpCnJldHVybiBuZXcgSC5aeShhLGIuQygi
+QDwwPiIpLktxKGMpLkMoIlp5PDEsMj4iKSl9LApqOmZ1bmN0aW9uKGEpe3JldHVybiBuZXcgSC5jKCJG
+aWVsZCAnIithKyInIGhhcyBiZWVuIGFzc2lnbmVkIGR1cmluZyBpbml0aWFsaXphdGlvbi4iKX0sCm9v
+OmZ1bmN0aW9uKGEpe3ZhciBzLHI9YV40OAppZihyPD05KXJldHVybiByCnM9YXwzMgppZig5Nzw9cyYm
+czw9MTAyKXJldHVybiBzLTg3CnJldHVybi0xfSwKY2I6ZnVuY3Rpb24oYSxiLGMpe3JldHVybiBhfSwK
+cUM6ZnVuY3Rpb24oYSxiLGMsZCl7UC5rMShiLCJzdGFydCIpCmlmKGMhPW51bGwpe1AuazEoYywiZW5k
+IikKaWYoYj5jKUgudihQLlRFKGIsMCxjLCJzdGFydCIsbnVsbCkpfXJldHVybiBuZXcgSC5uSChhLGIs
+YyxkLkMoIm5IPDA+IikpfSwKSzE6ZnVuY3Rpb24oYSxiLGMsZCl7aWYodC5XLmIoYSkpcmV0dXJuIG5l
+dyBILnh5KGEsYixjLkMoIkA8MD4iKS5LcShkKS5DKCJ4eTwxLDI+IikpCnJldHVybiBuZXcgSC5pMShh
+LGIsYy5DKCJAPDA+IikuS3EoZCkuQygiaTE8MSwyPiIpKX0sCmJLOmZ1bmN0aW9uKGEsYixjKXtpZih0
+LlcuYihhKSl7UC5rMShiLCJjb3VudCIpCnJldHVybiBuZXcgSC5kNShhLGIsYy5DKCJkNTwwPiIpKX1Q
+LmsxKGIsImNvdW50IikKcmV0dXJuIG5ldyBILkFNKGEsYixjLkMoIkFNPDA+IikpfSwKV3A6ZnVuY3Rp
+b24oKXtyZXR1cm4gbmV3IFAubGooIk5vIGVsZW1lbnQiKX0sCkFtOmZ1bmN0aW9uKCl7cmV0dXJuIG5l
+dyBQLmxqKCJUb28gbWFueSBlbGVtZW50cyIpfSwKYXI6ZnVuY3Rpb24oKXtyZXR1cm4gbmV3IFAubGoo
+IlRvbyBmZXcgZWxlbWVudHMiKX0sCkJSOmZ1bmN0aW9uIEJSKCl7fSwKZVQ6ZnVuY3Rpb24gZVQoYSxi
+KXt0aGlzLmE9YQp0aGlzLiR0aT1ifSwKWnk6ZnVuY3Rpb24gWnkoYSxiKXt0aGlzLmE9YQp0aGlzLiR0
+aT1ifSwKb2w6ZnVuY3Rpb24gb2woYSxiKXt0aGlzLmE9YQp0aGlzLiR0aT1ifSwKVXE6ZnVuY3Rpb24g
+VXEoKXt9LApqVjpmdW5jdGlvbiBqVihhLGIpe3RoaXMuYT1hCnRoaXMuJHRpPWJ9LApjOmZ1bmN0aW9u
+IGMoYSl7dGhpcy5hPWF9LApxajpmdW5jdGlvbiBxaihhKXt0aGlzLmE9YX0sCmJROmZ1bmN0aW9uIGJR
+KCl7fSwKYUw6ZnVuY3Rpb24gYUwoKXt9LApuSDpmdW5jdGlvbiBuSChhLGIsYyxkKXt2YXIgXz10aGlz
+Cl8uYT1hCl8uYj1iCl8uYz1jCl8uJHRpPWR9LAphNzpmdW5jdGlvbiBhNyhhLGIsYyl7dmFyIF89dGhp
+cwpfLmE9YQpfLmI9YgpfLmM9MApfLmQ9bnVsbApfLiR0aT1jfSwKaTE6ZnVuY3Rpb24gaTEoYSxiLGMp
+e3RoaXMuYT1hCnRoaXMuYj1iCnRoaXMuJHRpPWN9LAp4eTpmdW5jdGlvbiB4eShhLGIsYyl7dGhpcy5h
+PWEKdGhpcy5iPWIKdGhpcy4kdGk9Y30sCk1IOmZ1bmN0aW9uIE1IKGEsYixjKXt2YXIgXz10aGlzCl8u
+YT1udWxsCl8uYj1hCl8uYz1iCl8uJHRpPWN9LApsSjpmdW5jdGlvbiBsSihhLGIsYyl7dGhpcy5hPWEK
+dGhpcy5iPWIKdGhpcy4kdGk9Y30sClU1OmZ1bmN0aW9uIFU1KGEsYixjKXt0aGlzLmE9YQp0aGlzLmI9
+Ygp0aGlzLiR0aT1jfSwKU086ZnVuY3Rpb24gU08oYSxiLGMpe3RoaXMuYT1hCnRoaXMuYj1iCnRoaXMu
+JHRpPWN9LApBTTpmdW5jdGlvbiBBTShhLGIsYyl7dGhpcy5hPWEKdGhpcy5iPWIKdGhpcy4kdGk9Y30s
+CmQ1OmZ1bmN0aW9uIGQ1KGEsYixjKXt0aGlzLmE9YQp0aGlzLmI9Ygp0aGlzLiR0aT1jfSwKVTE6ZnVu
+Y3Rpb24gVTEoYSxiLGMpe3RoaXMuYT1hCnRoaXMuYj1iCnRoaXMuJHRpPWN9LApNQjpmdW5jdGlvbiBN
+QihhKXt0aGlzLiR0aT1hfSwKRnU6ZnVuY3Rpb24gRnUoYSl7dGhpcy4kdGk9YX0sCnU2OmZ1bmN0aW9u
+IHU2KGEsYil7dGhpcy5hPWEKdGhpcy4kdGk9Yn0sCkpCOmZ1bmN0aW9uIEpCKGEsYil7dGhpcy5hPWEK
+dGhpcy4kdGk9Yn0sClNVOmZ1bmN0aW9uIFNVKCl7fSwKUmU6ZnVuY3Rpb24gUmUoKXt9LAp3MjpmdW5j
+dGlvbiB3Migpe30sCnd2OmZ1bmN0aW9uIHd2KGEpe3RoaXMuYT1hfSwKUUM6ZnVuY3Rpb24gUUMoKXt9
+LApkYzpmdW5jdGlvbigpe3Rocm93IEguYihQLkw0KCJDYW5ub3QgbW9kaWZ5IHVubW9kaWZpYWJsZSBN
+YXAiKSl9LApOUTpmdW5jdGlvbihhKXt2YXIgcyxyPXYubWFuZ2xlZEdsb2JhbE5hbWVzW2FdCmlmKHIh
+PW51bGwpcmV0dXJuIHIKcz0ibWluaWZpZWQ6IithCnJldHVybiBzfSwKd1Y6ZnVuY3Rpb24oYSxiKXt2
+YXIgcwppZihiIT1udWxsKXtzPWIueAppZihzIT1udWxsKXJldHVybiBzfXJldHVybiB0LmFVLmIoYSl9
+LApFajpmdW5jdGlvbihhKXt2YXIgcwppZih0eXBlb2YgYT09InN0cmluZyIpcmV0dXJuIGEKaWYodHlw
+ZW9mIGE9PSJudW1iZXIiKXtpZihhIT09MClyZXR1cm4iIithfWVsc2UgaWYoITA9PT1hKXJldHVybiJ0
+cnVlIgplbHNlIGlmKCExPT09YSlyZXR1cm4iZmFsc2UiCmVsc2UgaWYoYT09bnVsbClyZXR1cm4ibnVs
+bCIKcz1KLncoYSkKcmV0dXJuIHN9LAplUTpmdW5jdGlvbihhKXt2YXIgcz1hLiRpZGVudGl0eUhhc2gK
+aWYocz09bnVsbCl7cz1NYXRoLnJhbmRvbSgpKjB4M2ZmZmZmZmZ8MAphLiRpZGVudGl0eUhhc2g9c31y
+ZXR1cm4gc30sCkhwOmZ1bmN0aW9uKGEsYil7dmFyIHMscixxLHAsbyxuPW51bGwsbT0vXlxzKlsrLV0/
+KCgweFthLWYwLTldKyl8KFxkKyl8KFthLXowLTldKykpXHMqJC9pLmV4ZWMoYSkKaWYobT09bnVsbCly
+ZXR1cm4gbgppZigzPj1tLmxlbmd0aClyZXR1cm4gSC5PSChtLDMpCnM9bVszXQppZihiPT1udWxsKXtp
+ZihzIT1udWxsKXJldHVybiBwYXJzZUludChhLDEwKQppZihtWzJdIT1udWxsKXJldHVybiBwYXJzZUlu
+dChhLDE2KQpyZXR1cm4gbn1pZihiPDJ8fGI+MzYpdGhyb3cgSC5iKFAuVEUoYiwyLDM2LCJyYWRpeCIs
+bikpCmlmKGI9PT0xMCYmcyE9bnVsbClyZXR1cm4gcGFyc2VJbnQoYSwxMCkKaWYoYjwxMHx8cz09bnVs
+bCl7cj1iPD0xMD80NytiOjg2K2IKcT1tWzFdCmZvcihwPXEubGVuZ3RoLG89MDtvPHA7KytvKWlmKChD
+LnhCLlcocSxvKXwzMik+cilyZXR1cm4gbn1yZXR1cm4gcGFyc2VJbnQoYSxiKX0sCmxoOmZ1bmN0aW9u
+KGEpe3JldHVybiBILkg1KGEpfSwKSDU6ZnVuY3Rpb24oYSl7dmFyIHMscixxLHAKaWYoYSBpbnN0YW5j
+ZW9mIFAuTWgpcmV0dXJuIEguZG0oSC56SyhhKSxudWxsKQppZihKLmlhKGEpPT09Qy5Pa3x8dC5iSi5i
+KGEpKXtzPUMuTzQoYSkKcj1zIT09Ik9iamVjdCImJnMhPT0iIgppZihyKXJldHVybiBzCnE9YS5jb25z
+dHJ1Y3RvcgppZih0eXBlb2YgcT09ImZ1bmN0aW9uIil7cD1xLm5hbWUKaWYodHlwZW9mIHA9PSJzdHJp
+bmciKXI9cCE9PSJPYmplY3QiJiZwIT09IiIKZWxzZSByPSExCmlmKHIpcmV0dXJuIHB9fXJldHVybiBI
+LmRtKEgueksoYSksbnVsbCl9LApNMDpmdW5jdGlvbigpe2lmKCEhc2VsZi5sb2NhdGlvbilyZXR1cm4g
+c2VsZi5sb2NhdGlvbi5ocmVmCnJldHVybiBudWxsfSwKZnc6ZnVuY3Rpb24oYSxiLGMpe3ZhciBzLHIs
+cSxwCmlmKGM8PTUwMCYmYj09PTAmJmM9PT1hLmxlbmd0aClyZXR1cm4gU3RyaW5nLmZyb21DaGFyQ29k
+ZS5hcHBseShudWxsLGEpCmZvcihzPWIscj0iIjtzPGM7cz1xKXtxPXMrNTAwCnA9cTxjP3E6YwpyKz1T
+dHJpbmcuZnJvbUNoYXJDb2RlLmFwcGx5KG51bGwsYS5zdWJhcnJheShzLHApKX1yZXR1cm4gcn0sCkx3
+OmZ1bmN0aW9uKGEpe3ZhciBzCmlmKDA8PWEpe2lmKGE8PTY1NTM1KXJldHVybiBTdHJpbmcuZnJvbUNo
+YXJDb2RlKGEpCmlmKGE8PTExMTQxMTEpe3M9YS02NTUzNgpyZXR1cm4gU3RyaW5nLmZyb21DaGFyQ29k
+ZSgoQy5qbi53RyhzLDEwKXw1NTI5Nik+Pj4wLHMmMTAyM3w1NjMyMCl9fXRocm93IEguYihQLlRFKGEs
+MCwxMTE0MTExLG51bGwsbnVsbCkpfSwKbzI6ZnVuY3Rpb24oYSl7aWYoYS5kYXRlPT09dm9pZCAwKWEu
+ZGF0ZT1uZXcgRGF0ZShhLmEpCnJldHVybiBhLmRhdGV9LAp0SjpmdW5jdGlvbihhKXt2YXIgcz1ILm8y
+KGEpLmdldEZ1bGxZZWFyKCkrMApyZXR1cm4gc30sCk5TOmZ1bmN0aW9uKGEpe3ZhciBzPUgubzIoYSku
+Z2V0TW9udGgoKSsxCnJldHVybiBzfSwKakE6ZnVuY3Rpb24oYSl7dmFyIHM9SC5vMihhKS5nZXREYXRl
+KCkrMApyZXR1cm4gc30sCklYOmZ1bmN0aW9uKGEpe3ZhciBzPUgubzIoYSkuZ2V0SG91cnMoKSswCnJl
+dHVybiBzfSwKY2g6ZnVuY3Rpb24oYSl7dmFyIHM9SC5vMihhKS5nZXRNaW51dGVzKCkrMApyZXR1cm4g
+c30sCkpkOmZ1bmN0aW9uKGEpe3ZhciBzPUgubzIoYSkuZ2V0U2Vjb25kcygpKzAKcmV0dXJuIHN9LApv
+MTpmdW5jdGlvbihhKXt2YXIgcz1ILm8yKGEpLmdldE1pbGxpc2Vjb25kcygpKzAKcmV0dXJuIHN9LAp6
+bzpmdW5jdGlvbihhLGIsYyl7dmFyIHMscixxPXt9CnEuYT0wCnM9W10Kcj1bXQpxLmE9Yi5sZW5ndGgK
+Qy5ObS5GVihzLGIpCnEuYj0iIgppZihjIT1udWxsJiZjLmEhPT0wKWMuSygwLG5ldyBILkNqKHEscixz
+KSkKIiIrcS5hCnJldHVybiBKLkp5KGEsbmV3IEguTEkoQy5UZSwwLHMsciwwKSl9LApFazpmdW5jdGlv
+bihhLGIsYyl7dmFyIHMscixxLHAKaWYoYiBpbnN0YW5jZW9mIEFycmF5KXM9Yz09bnVsbHx8Yy5hPT09
+MAplbHNlIHM9ITEKaWYocyl7cj1iCnE9ci5sZW5ndGgKaWYocT09PTApe2lmKCEhYS4kMClyZXR1cm4g
+YS4kMCgpfWVsc2UgaWYocT09PTEpe2lmKCEhYS4kMSlyZXR1cm4gYS4kMShyWzBdKX1lbHNlIGlmKHE9
+PT0yKXtpZighIWEuJDIpcmV0dXJuIGEuJDIoclswXSxyWzFdKX1lbHNlIGlmKHE9PT0zKXtpZighIWEu
+JDMpcmV0dXJuIGEuJDMoclswXSxyWzFdLHJbMl0pfWVsc2UgaWYocT09PTQpe2lmKCEhYS4kNClyZXR1
+cm4gYS4kNChyWzBdLHJbMV0sclsyXSxyWzNdKX1lbHNlIGlmKHE9PT01KWlmKCEhYS4kNSlyZXR1cm4g
+YS4kNShyWzBdLHJbMV0sclsyXSxyWzNdLHJbNF0pCnA9YVsiIisiJCIrcV0KaWYocCE9bnVsbClyZXR1
+cm4gcC5hcHBseShhLHIpfXJldHVybiBILmUxKGEsYixjKX0sCmUxOmZ1bmN0aW9uKGEsYixjKXt2YXIg
+cyxyLHEscCxvLG4sbSxsLGssaixpPWIgaW5zdGFuY2VvZiBBcnJheT9iOlAuQ0goYiwhMCx0LnopLGg9
+aS5sZW5ndGgsZz1hLiRSCmlmKGg8ZylyZXR1cm4gSC56byhhLGksYykKcz1hLiRECnI9cz09bnVsbApx
+PSFyP3MoKTpudWxsCnA9Si5pYShhKQpvPXAuJEMKaWYodHlwZW9mIG89PSJzdHJpbmciKW89cFtvXQpp
+ZihyKXtpZihjIT1udWxsJiZjLmEhPT0wKXJldHVybiBILnpvKGEsaSxjKQppZihoPT09ZylyZXR1cm4g
+by5hcHBseShhLGkpCnJldHVybiBILnpvKGEsaSxjKX1pZihxIGluc3RhbmNlb2YgQXJyYXkpe2lmKGMh
+PW51bGwmJmMuYSE9PTApcmV0dXJuIEguem8oYSxpLGMpCmlmKGg+ZytxLmxlbmd0aClyZXR1cm4gSC56
+byhhLGksbnVsbCkKQy5ObS5GVihpLHEuc2xpY2UoaC1nKSkKcmV0dXJuIG8uYXBwbHkoYSxpKX1lbHNl
+e2lmKGg+ZylyZXR1cm4gSC56byhhLGksYykKbj1PYmplY3Qua2V5cyhxKQppZihjPT1udWxsKWZvcihy
+PW4ubGVuZ3RoLG09MDttPG4ubGVuZ3RoO24ubGVuZ3RoPT09cnx8KDAsSC5saykobiksKyttKXtsPXFb
+SC5uKG5bbV0pXQppZihDLk52PT09bClyZXR1cm4gSC56byhhLGksYykKQy5ObS5pKGksbCl9ZWxzZXtm
+b3Iocj1uLmxlbmd0aCxrPTAsbT0wO208bi5sZW5ndGg7bi5sZW5ndGg9PT1yfHwoMCxILmxrKShuKSwr
+K20pe2o9SC5uKG5bbV0pCmlmKGMueDQoaikpeysrawpDLk5tLmkoaSxjLnEoMCxqKSl9ZWxzZXtsPXFb
+al0KaWYoQy5Odj09PWwpcmV0dXJuIEguem8oYSxpLGMpCkMuTm0uaShpLGwpfX1pZihrIT09Yy5hKXJl
+dHVybiBILnpvKGEsaSxjKX1yZXR1cm4gby5hcHBseShhLGkpfX0sCnBZOmZ1bmN0aW9uKGEpe3Rocm93
+IEguYihILnRMKGEpKX0sCk9IOmZ1bmN0aW9uKGEsYil7aWYoYT09bnVsbClKLkhtKGEpCnRocm93IEgu
+YihILnUoYSxiKSl9LAp1OmZ1bmN0aW9uKGEsYil7dmFyIHMscj0iaW5kZXgiCmlmKCFILm9rKGIpKXJl
+dHVybiBuZXcgUC5BVCghMCxiLHIsbnVsbCkKcz1ILklaKEouSG0oYSkpCmlmKGI8MHx8Yj49cylyZXR1
+cm4gUC5DZihiLGEscixudWxsLHMpCnJldHVybiBQLk83KGIscil9LAphdTpmdW5jdGlvbihhLGIsYyl7
+aWYoYT5jKXJldHVybiBQLlRFKGEsMCxjLCJzdGFydCIsbnVsbCkKaWYoYiE9bnVsbClpZihiPGF8fGI+
+YylyZXR1cm4gUC5URShiLGEsYywiZW5kIixudWxsKQpyZXR1cm4gbmV3IFAuQVQoITAsYiwiZW5kIixu
+dWxsKX0sCnRMOmZ1bmN0aW9uKGEpe3JldHVybiBuZXcgUC5BVCghMCxhLG51bGwsbnVsbCl9LApiOmZ1
+bmN0aW9uKGEpe3ZhciBzLHIKaWYoYT09bnVsbClhPW5ldyBQLkYoKQpzPW5ldyBFcnJvcigpCnMuZGFy
+dEV4Y2VwdGlvbj1hCnI9SC5oCmlmKCJkZWZpbmVQcm9wZXJ0eSIgaW4gT2JqZWN0KXtPYmplY3QuZGVm
+aW5lUHJvcGVydHkocywibWVzc2FnZSIse2dldDpyfSkKcy5uYW1lPSIifWVsc2Ugcy50b1N0cmluZz1y
+CnJldHVybiBzfSwKaDpmdW5jdGlvbigpe3JldHVybiBKLncodGhpcy5kYXJ0RXhjZXB0aW9uKX0sCnY6
+ZnVuY3Rpb24oYSl7dGhyb3cgSC5iKGEpfSwKbGs6ZnVuY3Rpb24oYSl7dGhyb3cgSC5iKFAuYTQoYSkp
+fSwKY006ZnVuY3Rpb24oYSl7dmFyIHMscixxLHAsbyxuCmE9SC5lQShhLnJlcGxhY2UoU3RyaW5nKHt9
+KSwiJHJlY2VpdmVyJCIpKQpzPWEubWF0Y2goL1xcXCRbYS16QS1aXStcXFwkL2cpCmlmKHM9PW51bGwp
+cz1ILlFJKFtdLHQucykKcj1zLmluZGV4T2YoIlxcJGFyZ3VtZW50c1xcJCIpCnE9cy5pbmRleE9mKCJc
+XCRhcmd1bWVudHNFeHByXFwkIikKcD1zLmluZGV4T2YoIlxcJGV4cHJcXCQiKQpvPXMuaW5kZXhPZigi
+XFwkbWV0aG9kXFwkIikKbj1zLmluZGV4T2YoIlxcJHJlY2VpdmVyXFwkIikKcmV0dXJuIG5ldyBILmY5
+KGEucmVwbGFjZShuZXcgUmVnRXhwKCJcXFxcXFwkYXJndW1lbnRzXFxcXFxcJCIsImciKSwiKCg/Onh8
+W154XSkqKSIpLnJlcGxhY2UobmV3IFJlZ0V4cCgiXFxcXFxcJGFyZ3VtZW50c0V4cHJcXFxcXFwkIiwi
+ZyIpLCIoKD86eHxbXnhdKSopIikucmVwbGFjZShuZXcgUmVnRXhwKCJcXFxcXFwkZXhwclxcXFxcXCQi
+LCJnIiksIigoPzp4fFteeF0pKikiKS5yZXBsYWNlKG5ldyBSZWdFeHAoIlxcXFxcXCRtZXRob2RcXFxc
+XFwkIiwiZyIpLCIoKD86eHxbXnhdKSopIikucmVwbGFjZShuZXcgUmVnRXhwKCJcXFxcXFwkcmVjZWl2
+ZXJcXFxcXFwkIiwiZyIpLCIoKD86eHxbXnhdKSopIikscixxLHAsbyxuKX0sClM3OmZ1bmN0aW9uKGEp
+e3JldHVybiBmdW5jdGlvbigkZXhwciQpe3ZhciAkYXJndW1lbnRzRXhwciQ9IiRhcmd1bWVudHMkIgp0
+cnl7JGV4cHIkLiRtZXRob2QkKCRhcmd1bWVudHNFeHByJCl9Y2F0Y2gocyl7cmV0dXJuIHMubWVzc2Fn
+ZX19KGEpfSwKTWo6ZnVuY3Rpb24oYSl7cmV0dXJuIGZ1bmN0aW9uKCRleHByJCl7dHJ5eyRleHByJC4k
+bWV0aG9kJH1jYXRjaChzKXtyZXR1cm4gcy5tZXNzYWdlfX0oYSl9LApUMzpmdW5jdGlvbihhLGIpe3Zh
+ciBzPWI9PW51bGwscj1zP251bGw6Yi5tZXRob2QKcmV0dXJuIG5ldyBILmF6KGEscixzP251bGw6Yi5y
+ZWNlaXZlcil9LApSdTpmdW5jdGlvbihhKXtpZihhPT1udWxsKXJldHVybiBuZXcgSC50ZShhKQppZihh
+IGluc3RhbmNlb2YgSC5icSlyZXR1cm4gSC50VyhhLHQuSy5hKGEuYSkpCmlmKHR5cGVvZiBhIT09Im9i
+amVjdCIpcmV0dXJuIGEKaWYoImRhcnRFeGNlcHRpb24iIGluIGEpcmV0dXJuIEgudFcoYSxhLmRhcnRF
+eGNlcHRpb24pCnJldHVybiBILnRsKGEpfSwKdFc6ZnVuY3Rpb24oYSxiKXtpZih0Lm0uYihiKSlpZihi
+LiR0aHJvd25Kc0Vycm9yPT1udWxsKWIuJHRocm93bkpzRXJyb3I9YQpyZXR1cm4gYn0sCnRsOmZ1bmN0
+aW9uKGEpe3ZhciBzLHIscSxwLG8sbixtLGwsayxqLGksaCxnLGYsZT1udWxsCmlmKCEoIm1lc3NhZ2Ui
+IGluIGEpKXJldHVybiBhCnM9YS5tZXNzYWdlCmlmKCJudW1iZXIiIGluIGEmJnR5cGVvZiBhLm51bWJl
+cj09Im51bWJlciIpe3I9YS5udW1iZXIKcT1yJjY1NTM1CmlmKChDLmpuLndHKHIsMTYpJjgxOTEpPT09
+MTApc3dpdGNoKHEpe2Nhc2UgNDM4OnJldHVybiBILnRXKGEsSC5UMyhILkVqKHMpKyIgKEVycm9yICIr
+cSsiKSIsZSkpCmNhc2UgNDQ1OmNhc2UgNTAwNzpwPUguRWoocykrIiAoRXJyb3IgIitxKyIpIgpyZXR1
+cm4gSC50VyhhLG5ldyBILlcwKHAsZSkpfX1pZihhIGluc3RhbmNlb2YgVHlwZUVycm9yKXtvPSQuU24o
+KQpuPSQubHEoKQptPSQuTjkoKQpsPSQuaUkoKQprPSQuVU4oKQpqPSQuWmgoKQppPSQuck4oKQokLmMz
+KCkKaD0kLkhLKCkKZz0kLnIxKCkKZj1vLnFTKHMpCmlmKGYhPW51bGwpcmV0dXJuIEgudFcoYSxILlQz
+KEgubihzKSxmKSkKZWxzZXtmPW4ucVMocykKaWYoZiE9bnVsbCl7Zi5tZXRob2Q9ImNhbGwiCnJldHVy
+biBILnRXKGEsSC5UMyhILm4ocyksZikpfWVsc2V7Zj1tLnFTKHMpCmlmKGY9PW51bGwpe2Y9bC5xUyhz
+KQppZihmPT1udWxsKXtmPWsucVMocykKaWYoZj09bnVsbCl7Zj1qLnFTKHMpCmlmKGY9PW51bGwpe2Y9
+aS5xUyhzKQppZihmPT1udWxsKXtmPWwucVMocykKaWYoZj09bnVsbCl7Zj1oLnFTKHMpCmlmKGY9PW51
+bGwpe2Y9Zy5xUyhzKQpwPWYhPW51bGx9ZWxzZSBwPSEwfWVsc2UgcD0hMH1lbHNlIHA9ITB9ZWxzZSBw
+PSEwfWVsc2UgcD0hMH1lbHNlIHA9ITB9ZWxzZSBwPSEwCmlmKHApe0gubihzKQpyZXR1cm4gSC50Vyhh
+LG5ldyBILlcwKHMsZj09bnVsbD9lOmYubWV0aG9kKSl9fX1yZXR1cm4gSC50VyhhLG5ldyBILnZWKHR5
+cGVvZiBzPT0ic3RyaW5nIj9zOiIiKSl9aWYoYSBpbnN0YW5jZW9mIFJhbmdlRXJyb3Ipe2lmKHR5cGVv
+ZiBzPT0ic3RyaW5nIiYmcy5pbmRleE9mKCJjYWxsIHN0YWNrIikhPT0tMSlyZXR1cm4gbmV3IFAuS1ko
+KQpzPWZ1bmN0aW9uKGIpe3RyeXtyZXR1cm4gU3RyaW5nKGIpfWNhdGNoKGQpe31yZXR1cm4gbnVsbH0o
+YSkKcmV0dXJuIEgudFcoYSxuZXcgUC5BVCghMSxlLGUsdHlwZW9mIHM9PSJzdHJpbmciP3MucmVwbGFj
+ZSgvXlJhbmdlRXJyb3I6XHMqLywiIik6cykpfWlmKHR5cGVvZiBJbnRlcm5hbEVycm9yPT0iZnVuY3Rp
+b24iJiZhIGluc3RhbmNlb2YgSW50ZXJuYWxFcnJvcilpZih0eXBlb2Ygcz09InN0cmluZyImJnM9PT0i
+dG9vIG11Y2ggcmVjdXJzaW9uIilyZXR1cm4gbmV3IFAuS1koKQpyZXR1cm4gYX0sCnRzOmZ1bmN0aW9u
+KGEpe3ZhciBzCmlmKGEgaW5zdGFuY2VvZiBILmJxKXJldHVybiBhLmIKaWYoYT09bnVsbClyZXR1cm4g
+bmV3IEguWE8oYSkKcz1hLiRjYWNoZWRUcmFjZQppZihzIT1udWxsKXJldHVybiBzCnJldHVybiBhLiRj
+YWNoZWRUcmFjZT1uZXcgSC5YTyhhKX0sCkI3OmZ1bmN0aW9uKGEsYil7dmFyIHMscixxLHA9YS5sZW5n
+dGgKZm9yKHM9MDtzPHA7cz1xKXtyPXMrMQpxPXIrMQpiLlk1KDAsYVtzXSxhW3JdKX1yZXR1cm4gYn0s
+CmZ0OmZ1bmN0aW9uKGEsYixjLGQsZSxmKXt0LlkuYShhKQpzd2l0Y2goSC5JWihiKSl7Y2FzZSAwOnJl
+dHVybiBhLiQwKCkKY2FzZSAxOnJldHVybiBhLiQxKGMpCmNhc2UgMjpyZXR1cm4gYS4kMihjLGQpCmNh
+c2UgMzpyZXR1cm4gYS4kMyhjLGQsZSkKY2FzZSA0OnJldHVybiBhLiQ0KGMsZCxlLGYpfXRocm93IEgu
+YihuZXcgUC5DRCgiVW5zdXBwb3J0ZWQgbnVtYmVyIG9mIGFyZ3VtZW50cyBmb3Igd3JhcHBlZCBjbG9z
+dXJlIikpfSwKdFI6ZnVuY3Rpb24oYSxiKXt2YXIgcwppZihhPT1udWxsKXJldHVybiBudWxsCnM9YS4k
+aWRlbnRpdHkKaWYoISFzKXJldHVybiBzCnM9ZnVuY3Rpb24oYyxkLGUpe3JldHVybiBmdW5jdGlvbihm
+LGcsaCxpKXtyZXR1cm4gZShjLGQsZixnLGgsaSl9fShhLGIsSC5mdCkKYS4kaWRlbnRpdHk9cwpyZXR1
+cm4gc30sCmlBOmZ1bmN0aW9uKGEyKXt2YXIgcyxyLHEscCxvLG4sbSxsLGssaixpPWEyLmNvLGg9YTIu
+aVMsZz1hMi5pSSxmPWEyLm5EQSxlPWEyLmFJLGQ9YTIuZnMsYz1hMi5jcyxiPWRbMF0sYT1jWzBdLGEw
+PWlbYl0sYTE9YTIuZlQKYTEudG9TdHJpbmcKcz1oP09iamVjdC5jcmVhdGUobmV3IEguengoKS5jb25z
+dHJ1Y3Rvci5wcm90b3R5cGUpOk9iamVjdC5jcmVhdGUobmV3IEguclQobnVsbCxudWxsKS5jb25zdHJ1
+Y3Rvci5wcm90b3R5cGUpCnMuJGluaXRpYWxpemU9cy5jb25zdHJ1Y3RvcgppZihoKXI9ZnVuY3Rpb24g
+c3RhdGljX3RlYXJfb2ZmKCl7dGhpcy4kaW5pdGlhbGl6ZSgpfQplbHNle3E9JC55agppZih0eXBlb2Yg
+cSE9PSJudW1iZXIiKXJldHVybiBxLmgoKQokLnlqPXErMQpxPW5ldyBGdW5jdGlvbigiYSxiIitxLCJ0
+aGlzLiRpbml0aWFsaXplKGEsYiIrcSsiKSIpCnI9cX1zLmNvbnN0cnVjdG9yPXIKci5wcm90b3R5cGU9
+cwpzLiRfbmFtZT1iCnMuJF90YXJnZXQ9YTAKcT0haAppZihxKXA9SC5ieChiLGEwLGcsZikKZWxzZXtz
+LiRzdGF0aWNfbmFtZT1iCnA9YTB9cy4kUz1ILmltKGExLGgsZykKc1thXT1wCmZvcihvPXAsbj0xO248
+ZC5sZW5ndGg7KytuKXttPWRbbl0KaWYodHlwZW9mIG09PSJzdHJpbmciKXtsPWlbbV0Kaz1tCm09bH1l
+bHNlIGs9IiIKaj1jW25dCmlmKGohPW51bGwpe2lmKHEpbT1ILmJ4KGssbSxnLGYpCnNbal09bX1pZihu
+PT09ZSlvPW19cy4kQz1vCnMuJFI9YTIuckMKcy4kRD1hMi5kVgpyZXR1cm4gcn0sCmltOmZ1bmN0aW9u
+KGEsYixjKXt2YXIgcwppZih0eXBlb2YgYT09Im51bWJlciIpcmV0dXJuIGZ1bmN0aW9uKGQsZSl7cmV0
+dXJuIGZ1bmN0aW9uKCl7cmV0dXJuIGQoZSl9fShILkJwLGEpCmlmKHR5cGVvZiBhPT0ic3RyaW5nIil7
+aWYoYil0aHJvdyBILmIoIkNhbm5vdCBjb21wdXRlIHNpZ25hdHVyZSBmb3Igc3RhdGljIHRlYXJvZmYu
+IikKcz1jP0guUFc6SC5UbgpyZXR1cm4gZnVuY3Rpb24oZCxlKXtyZXR1cm4gZnVuY3Rpb24oKXtyZXR1
cm4gZSh0aGlzLGQpfX0oYSxzKX10aHJvdyBILmIoIkVycm9yIGluIGZ1bmN0aW9uVHlwZSBvZiB0ZWFy
-b2ZmIil9LAp2cTpmdW5jdGlvbihhLGIsYyxkKXt2YXIgcz1ILkRWCnN3aXRjaChILm9UKGIpPy0xOmEp
-e2Nhc2UgMDpyZXR1cm4gZnVuY3Rpb24oZSxmKXtyZXR1cm4gZnVuY3Rpb24oKXtyZXR1cm4gZih0aGlz
-KVtlXSgpfX0oYyxzKQpjYXNlIDE6cmV0dXJuIGZ1bmN0aW9uKGUsZil7cmV0dXJuIGZ1bmN0aW9uKGcp
-e3JldHVybiBmKHRoaXMpW2VdKGcpfX0oYyxzKQpjYXNlIDI6cmV0dXJuIGZ1bmN0aW9uKGUsZil7cmV0
-dXJuIGZ1bmN0aW9uKGcsaCl7cmV0dXJuIGYodGhpcylbZV0oZyxoKX19KGMscykKY2FzZSAzOnJldHVy
-biBmdW5jdGlvbihlLGYpe3JldHVybiBmdW5jdGlvbihnLGgsaSl7cmV0dXJuIGYodGhpcylbZV0oZyxo
-LGkpfX0oYyxzKQpjYXNlIDQ6cmV0dXJuIGZ1bmN0aW9uKGUsZil7cmV0dXJuIGZ1bmN0aW9uKGcsaCxp
-LGope3JldHVybiBmKHRoaXMpW2VdKGcsaCxpLGopfX0oYyxzKQpjYXNlIDU6cmV0dXJuIGZ1bmN0aW9u
-KGUsZil7cmV0dXJuIGZ1bmN0aW9uKGcsaCxpLGosayl7cmV0dXJuIGYodGhpcylbZV0oZyxoLGksaixr
-KX19KGMscykKZGVmYXVsdDpyZXR1cm4gZnVuY3Rpb24oZSxmKXtyZXR1cm4gZnVuY3Rpb24oKXtyZXR1
-cm4gZS5hcHBseShmKHRoaXMpLGFyZ3VtZW50cyl9fShkLHMpfX0sCmJ4OmZ1bmN0aW9uKGEsYixjLGQp
-e3ZhciBzLHIscSxwLG8KaWYoSC5vVChjKSlyZXR1cm4gSC5IZihhLGIsZCkKcz1iLmxlbmd0aApyPUgu
-b1QoZCl8fHM+PTI3CmlmKHIpcmV0dXJuIEgudnEocyxkLGEsYikKaWYocz09PTApe3I9JC55agppZih0
-eXBlb2YgciE9PSJudW1iZXIiKXJldHVybiByLmgoKQokLnlqPXIrMQpxPSJzZWxmIityCnI9InJldHVy
-biBmdW5jdGlvbigpe3ZhciAiK3ErIiA9IHRoaXMuIgpwPSQuV1cKcmV0dXJuIG5ldyBGdW5jdGlvbihy
-KyhwPT1udWxsPyQuV1c9SC5tOSgic2VsZiIpOnApKyI7cmV0dXJuICIrcSsiLiIrSC5FaihhKSsiKCk7
-fSIpKCl9bz0iYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoiLnNwbGl0KCIiKS5zcGxpY2UoMCxzKS5q
-b2luKCIsIikKcj0kLnlqCmlmKHR5cGVvZiByIT09Im51bWJlciIpcmV0dXJuIHIuaCgpCiQueWo9cisx
-Cm8rPXIKcj0icmV0dXJuIGZ1bmN0aW9uKCIrbysiKXtyZXR1cm4gdGhpcy4iCnA9JC5XVwpyZXR1cm4g
-bmV3IEZ1bmN0aW9uKHIrKHA9PW51bGw/JC5XVz1ILm05KCJzZWxmIik6cCkrIi4iK0guRWooYSkrIigi
-K28rIik7fSIpKCl9LApaNDpmdW5jdGlvbihhLGIsYyxkKXt2YXIgcz1ILkRWLHI9SC55Uwpzd2l0Y2go
-SC5vVChiKT8tMTphKXtjYXNlIDA6dGhyb3cgSC5iKG5ldyBILkVxKCJJbnRlcmNlcHRlZCBmdW5jdGlv
-biB3aXRoIG5vIGFyZ3VtZW50cy4iKSkKY2FzZSAxOnJldHVybiBmdW5jdGlvbihlLGYsZyl7cmV0dXJu
-IGZ1bmN0aW9uKCl7cmV0dXJuIGYodGhpcylbZV0oZyh0aGlzKSl9fShjLHMscikKY2FzZSAyOnJldHVy
-biBmdW5jdGlvbihlLGYsZyl7cmV0dXJuIGZ1bmN0aW9uKGgpe3JldHVybiBmKHRoaXMpW2VdKGcodGhp
-cyksaCl9fShjLHMscikKY2FzZSAzOnJldHVybiBmdW5jdGlvbihlLGYsZyl7cmV0dXJuIGZ1bmN0aW9u
-KGgsaSl7cmV0dXJuIGYodGhpcylbZV0oZyh0aGlzKSxoLGkpfX0oYyxzLHIpCmNhc2UgNDpyZXR1cm4g
-ZnVuY3Rpb24oZSxmLGcpe3JldHVybiBmdW5jdGlvbihoLGksail7cmV0dXJuIGYodGhpcylbZV0oZyh0
-aGlzKSxoLGksail9fShjLHMscikKY2FzZSA1OnJldHVybiBmdW5jdGlvbihlLGYsZyl7cmV0dXJuIGZ1
-bmN0aW9uKGgsaSxqLGspe3JldHVybiBmKHRoaXMpW2VdKGcodGhpcyksaCxpLGosayl9fShjLHMscikK
-Y2FzZSA2OnJldHVybiBmdW5jdGlvbihlLGYsZyl7cmV0dXJuIGZ1bmN0aW9uKGgsaSxqLGssbCl7cmV0
-dXJuIGYodGhpcylbZV0oZyh0aGlzKSxoLGksaixrLGwpfX0oYyxzLHIpCmRlZmF1bHQ6cmV0dXJuIGZ1
-bmN0aW9uKGUsZixnLGgpe3JldHVybiBmdW5jdGlvbigpe2g9W2codGhpcyldCkFycmF5LnByb3RvdHlw
-ZS5wdXNoLmFwcGx5KGgsYXJndW1lbnRzKQpyZXR1cm4gZS5hcHBseShmKHRoaXMpLGgpfX0oZCxzLHIp
-fX0sCkhmOmZ1bmN0aW9uKGEsYixjKXt2YXIgcyxyLHEscCxvLG49JC5XVwppZihuPT1udWxsKW49JC5X
-Vz1ILm05KCJzZWxmIikKcz0kLmkwCmlmKHM9PW51bGwpcz0kLmkwPUgubTkoInJlY2VpdmVyIikKcj1i
-Lmxlbmd0aApxPUgub1QoYyl8fHI+PTI4CmlmKHEpcmV0dXJuIEguWjQocixjLGEsYikKaWYocj09PTEp
-e3E9InJldHVybiBmdW5jdGlvbigpe3JldHVybiB0aGlzLiIrbisiLiIrSC5FaihhKSsiKHRoaXMuIitz
-KyIpOyIKcD0kLnlqCmlmKHR5cGVvZiBwIT09Im51bWJlciIpcmV0dXJuIHAuaCgpCiQueWo9cCsxCnJl
-dHVybiBuZXcgRnVuY3Rpb24ocStwKyJ9IikoKX1vPSJhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5eiIu
-c3BsaXQoIiIpLnNwbGljZSgwLHItMSkuam9pbigiLCIpCnE9InJldHVybiBmdW5jdGlvbigiK28rIil7
-cmV0dXJuIHRoaXMuIituKyIuIitILkVqKGEpKyIodGhpcy4iK3MrIiwgIitvKyIpOyIKcD0kLnlqCmlm
-KHR5cGVvZiBwIT09Im51bWJlciIpcmV0dXJuIHAuaCgpCiQueWo9cCsxCnJldHVybiBuZXcgRnVuY3Rp
-b24ocStwKyJ9IikoKX0sClUyOmZ1bmN0aW9uKGEpe3JldHVybiBILmlBKGEpfSwKVG46ZnVuY3Rpb24o
-YSxiKXtyZXR1cm4gSC5jRSh2LnR5cGVVbml2ZXJzZSxILnpLKGEuYSksYil9LApQVzpmdW5jdGlvbihh
-LGIpe3JldHVybiBILmNFKHYudHlwZVVuaXZlcnNlLEgueksoYS5iKSxiKX0sCkRWOmZ1bmN0aW9uKGEp
-e3JldHVybiBhLmF9LAp5UzpmdW5jdGlvbihhKXtyZXR1cm4gYS5ifSwKbTk6ZnVuY3Rpb24oYSl7dmFy
-IHMscixxLHA9bmV3IEguclQoInNlbGYiLCJyZWNlaXZlciIpLG89Si5FcChPYmplY3QuZ2V0T3duUHJv
-cGVydHlOYW1lcyhwKSx0LlcpCmZvcihzPW8ubGVuZ3RoLHI9MDtyPHM7KytyKXtxPW9bcl0KaWYocFtx
-XT09PWEpcmV0dXJuIHF9dGhyb3cgSC5iKFAueFkoIkZpZWxkIG5hbWUgIithKyIgbm90IGZvdW5kLiIp
-KX0sCm9UOmZ1bmN0aW9uKGEpe2lmKGE9PW51bGwpSC5mTygiYm9vbGVhbiBleHByZXNzaW9uIG11c3Qg
-bm90IGJlIG51bGwiKQpyZXR1cm4gYX0sCmZPOmZ1bmN0aW9uKGEpe3Rocm93IEguYihuZXcgSC5rWShh
-KSl9LAphZzpmdW5jdGlvbihhKXt0aHJvdyBILmIobmV3IFAudDcoYSkpfSwKWWc6ZnVuY3Rpb24oYSl7
-cmV0dXJuIHYuZ2V0SXNvbGF0ZVRhZyhhKX0sCml3OmZ1bmN0aW9uKGEsYixjKXtPYmplY3QuZGVmaW5l
-UHJvcGVydHkoYSxiLHt2YWx1ZTpjLGVudW1lcmFibGU6ZmFsc2Usd3JpdGFibGU6dHJ1ZSxjb25maWd1
-cmFibGU6dHJ1ZX0pfSwKdzM6ZnVuY3Rpb24oYSl7dmFyIHMscixxLHAsbyxuPUguaCgkLk5GLiQxKGEp
-KSxtPSQubndbbl0KaWYobSE9bnVsbCl7T2JqZWN0LmRlZmluZVByb3BlcnR5KGEsdi5kaXNwYXRjaFBy
-b3BlcnR5TmFtZSx7dmFsdWU6bSxlbnVtZXJhYmxlOmZhbHNlLHdyaXRhYmxlOnRydWUsY29uZmlndXJh
-YmxlOnRydWV9KQpyZXR1cm4gbS5pfXM9JC52dltuXQppZihzIT1udWxsKXJldHVybiBzCnI9di5pbnRl
-cmNlcHRvcnNCeVRhZ1tuXQppZihyPT1udWxsKXtxPUguaygkLlRYLiQyKGEsbikpCmlmKHEhPW51bGwp
-e209JC5ud1txXQppZihtIT1udWxsKXtPYmplY3QuZGVmaW5lUHJvcGVydHkoYSx2LmRpc3BhdGNoUHJv
-cGVydHlOYW1lLHt2YWx1ZTptLGVudW1lcmFibGU6ZmFsc2Usd3JpdGFibGU6dHJ1ZSxjb25maWd1cmFi
-bGU6dHJ1ZX0pCnJldHVybiBtLml9cz0kLnZ2W3FdCmlmKHMhPW51bGwpcmV0dXJuIHMKcj12LmludGVy
-Y2VwdG9yc0J5VGFnW3FdCm49cX19aWYocj09bnVsbClyZXR1cm4gbnVsbApzPXIucHJvdG90eXBlCnA9
-blswXQppZihwPT09IiEiKXttPUguVmEocykKJC5ud1tuXT1tCk9iamVjdC5kZWZpbmVQcm9wZXJ0eShh
-LHYuZGlzcGF0Y2hQcm9wZXJ0eU5hbWUse3ZhbHVlOm0sZW51bWVyYWJsZTpmYWxzZSx3cml0YWJsZTp0
-cnVlLGNvbmZpZ3VyYWJsZTp0cnVlfSkKcmV0dXJuIG0uaX1pZihwPT09In4iKXskLnZ2W25dPXMKcmV0
-dXJuIHN9aWYocD09PSItIil7bz1ILlZhKHMpCk9iamVjdC5kZWZpbmVQcm9wZXJ0eShPYmplY3QuZ2V0
-UHJvdG90eXBlT2YoYSksdi5kaXNwYXRjaFByb3BlcnR5TmFtZSx7dmFsdWU6byxlbnVtZXJhYmxlOmZh
-bHNlLHdyaXRhYmxlOnRydWUsY29uZmlndXJhYmxlOnRydWV9KQpyZXR1cm4gby5pfWlmKHA9PT0iKyIp
-cmV0dXJuIEguTGMoYSxzKQppZihwPT09IioiKXRocm93IEguYihQLlNZKG4pKQppZih2LmxlYWZUYWdz
-W25dPT09dHJ1ZSl7bz1ILlZhKHMpCk9iamVjdC5kZWZpbmVQcm9wZXJ0eShPYmplY3QuZ2V0UHJvdG90
-eXBlT2YoYSksdi5kaXNwYXRjaFByb3BlcnR5TmFtZSx7dmFsdWU6byxlbnVtZXJhYmxlOmZhbHNlLHdy
-aXRhYmxlOnRydWUsY29uZmlndXJhYmxlOnRydWV9KQpyZXR1cm4gby5pfWVsc2UgcmV0dXJuIEguTGMo
-YSxzKX0sCkxjOmZ1bmN0aW9uKGEsYil7dmFyIHM9T2JqZWN0LmdldFByb3RvdHlwZU9mKGEpCk9iamVj
-dC5kZWZpbmVQcm9wZXJ0eShzLHYuZGlzcGF0Y2hQcm9wZXJ0eU5hbWUse3ZhbHVlOkouUXUoYixzLG51
-bGwsbnVsbCksZW51bWVyYWJsZTpmYWxzZSx3cml0YWJsZTp0cnVlLGNvbmZpZ3VyYWJsZTp0cnVlfSkK
-cmV0dXJuIGJ9LApWYTpmdW5jdGlvbihhKXtyZXR1cm4gSi5RdShhLCExLG51bGwsISFhLiRpWGopfSwK
-VkY6ZnVuY3Rpb24oYSxiLGMpe3ZhciBzPWIucHJvdG90eXBlCmlmKHYubGVhZlRhZ3NbYV09PT10cnVl
-KXJldHVybiBILlZhKHMpCmVsc2UgcmV0dXJuIEouUXUocyxjLG51bGwsbnVsbCl9LApYRDpmdW5jdGlv
-bigpe2lmKCEwPT09JC5CdilyZXR1cm4KJC5Cdj0hMApILloxKCl9LApaMTpmdW5jdGlvbigpe3ZhciBz
-LHIscSxwLG8sbixtLGwKJC5udz1PYmplY3QuY3JlYXRlKG51bGwpCiQudnY9T2JqZWN0LmNyZWF0ZShu
-dWxsKQpILmtPKCkKcz12LmludGVyY2VwdG9yc0J5VGFnCnI9T2JqZWN0LmdldE93blByb3BlcnR5TmFt
-ZXMocykKaWYodHlwZW9mIHdpbmRvdyE9InVuZGVmaW5lZCIpe3dpbmRvdwpxPWZ1bmN0aW9uKCl7fQpm
-b3IocD0wO3A8ci5sZW5ndGg7KytwKXtvPXJbcF0Kbj0kLng3LiQxKG8pCmlmKG4hPW51bGwpe209SC5W
-RihvLHNbb10sbikKaWYobSE9bnVsbCl7T2JqZWN0LmRlZmluZVByb3BlcnR5KG4sdi5kaXNwYXRjaFBy
-b3BlcnR5TmFtZSx7dmFsdWU6bSxlbnVtZXJhYmxlOmZhbHNlLHdyaXRhYmxlOnRydWUsY29uZmlndXJh
-YmxlOnRydWV9KQpxLnByb3RvdHlwZT1ufX19fWZvcihwPTA7cDxyLmxlbmd0aDsrK3Ape289cltwXQpp
-ZigvXltBLVphLXpfXS8udGVzdChvKSl7bD1zW29dCnNbIiEiK29dPWwKc1sifiIrb109bApzWyItIitv
-XT1sCnNbIisiK29dPWwKc1siKiIrb109bH19fSwKa086ZnVuY3Rpb24oKXt2YXIgcyxyLHEscCxvLG4s
-bT1DLllxKCkKbT1ILnVkKEMuS1UsSC51ZChDLmZRLEgudWQoQy5pNyxILnVkKEMuaTcsSC51ZChDLnhp
-LEgudWQoQy5kayxILnVkKEMud2IoQy5PNCksbSkpKSkpKSkKaWYodHlwZW9mIGRhcnROYXRpdmVEaXNw
-YXRjaEhvb2tzVHJhbnNmb3JtZXIhPSJ1bmRlZmluZWQiKXtzPWRhcnROYXRpdmVEaXNwYXRjaEhvb2tz
-VHJhbnNmb3JtZXIKaWYodHlwZW9mIHM9PSJmdW5jdGlvbiIpcz1bc10KaWYocy5jb25zdHJ1Y3Rvcj09
-QXJyYXkpZm9yKHI9MDtyPHMubGVuZ3RoOysrcil7cT1zW3JdCmlmKHR5cGVvZiBxPT0iZnVuY3Rpb24i
-KW09cShtKXx8bX19cD1tLmdldFRhZwpvPW0uZ2V0VW5rbm93blRhZwpuPW0ucHJvdG90eXBlRm9yVGFn
-CiQuTkY9bmV3IEguZEMocCkKJC5UWD1uZXcgSC53TihvKQokLng3PW5ldyBILlZYKG4pfSwKdWQ6ZnVu
-Y3Rpb24oYSxiKXtyZXR1cm4gYShiKXx8Yn0sCnY0OmZ1bmN0aW9uKGEsYixjLGQsZSxmKXt2YXIgcz1i
-PyJtIjoiIixyPWM/IiI6ImkiLHE9ZD8idSI6IiIscD1lPyJzIjoiIixvPWY/ImciOiIiLG49ZnVuY3Rp
-b24oZyxoKXt0cnl7cmV0dXJuIG5ldyBSZWdFeHAoZyxoKX1jYXRjaChtKXtyZXR1cm4gbX19KGEscyty
-K3ErcCtvKQppZihuIGluc3RhbmNlb2YgUmVnRXhwKXJldHVybiBuCnRocm93IEguYihQLnJyKCJJbGxl
-Z2FsIFJlZ0V4cCBwYXR0ZXJuICgiK1N0cmluZyhuKSsiKSIsYSxudWxsKSl9LApTUTpmdW5jdGlvbihh
-LGIsYyl7dmFyIHMKaWYodHlwZW9mIGI9PSJzdHJpbmciKXJldHVybiBhLmluZGV4T2YoYixjKT49MApl
-bHNlIGlmKGIgaW5zdGFuY2VvZiBILlZSKXtzPUMueEIueW4oYSxjKQpyZXR1cm4gYi5iLnRlc3Qocyl9
-ZWxzZXtzPUouRkwoYixDLnhCLnluKGEsYykpCnJldHVybiFzLmdsMChzKX19LApBNDpmdW5jdGlvbihh
-KXtpZihhLmluZGV4T2YoIiQiLDApPj0wKXJldHVybiBhLnJlcGxhY2UoL1wkL2csIiQkJCQiKQpyZXR1
-cm4gYX0sCmVBOmZ1bmN0aW9uKGEpe2lmKC9bW1xde30oKSorPy5cXF4kfF0vLnRlc3QoYSkpcmV0dXJu
-IGEucmVwbGFjZSgvW1tcXXt9KCkqKz8uXFxeJHxdL2csIlxcJCYiKQpyZXR1cm4gYX0sCnlzOmZ1bmN0
-aW9uKGEsYixjKXt2YXIgcz1ILm5NKGEsYixjKQpyZXR1cm4gc30sCm5NOmZ1bmN0aW9uKGEsYixjKXt2
-YXIgcyxyLHEscAppZihiPT09IiIpe2lmKGE9PT0iIilyZXR1cm4gYwpzPWEubGVuZ3RoCmZvcihyPWMs
-cT0wO3E8czsrK3Epcj1yK2FbcV0rYwpyZXR1cm4gci5jaGFyQ29kZUF0KDApPT0wP3I6cn1wPWEuaW5k
-ZXhPZihiLDApCmlmKHA8MClyZXR1cm4gYQppZihhLmxlbmd0aDw1MDB8fGMuaW5kZXhPZigiJCIsMCk+
-PTApcmV0dXJuIGEuc3BsaXQoYikuam9pbihjKQpyZXR1cm4gYS5yZXBsYWNlKG5ldyBSZWdFeHAoSC5l
-QShiKSwiZyIpLEguQTQoYykpfSwKUEQ6ZnVuY3Rpb24gUEQoYSxiKXt0aGlzLmE9YQp0aGlzLiR0aT1i
-fSwKV1U6ZnVuY3Rpb24gV1UoKXt9LApMUDpmdW5jdGlvbiBMUChhLGIsYyxkKXt2YXIgXz10aGlzCl8u
-YT1hCl8uYj1iCl8uYz1jCl8uJHRpPWR9LApYUjpmdW5jdGlvbiBYUihhLGIpe3RoaXMuYT1hCnRoaXMu
-JHRpPWJ9LApMSTpmdW5jdGlvbiBMSShhLGIsYyxkLGUpe3ZhciBfPXRoaXMKXy5hPWEKXy5jPWIKXy5k
-PWMKXy5lPWQKXy5mPWV9LApDajpmdW5jdGlvbiBDaihhLGIsYyl7dGhpcy5hPWEKdGhpcy5iPWIKdGhp
-cy5jPWN9LApmOTpmdW5jdGlvbiBmOShhLGIsYyxkLGUsZil7dmFyIF89dGhpcwpfLmE9YQpfLmI9Ygpf
-LmM9YwpfLmQ9ZApfLmU9ZQpfLmY9Zn0sClcwOmZ1bmN0aW9uIFcwKGEsYil7dGhpcy5hPWEKdGhpcy5i
-PWJ9LAphejpmdW5jdGlvbiBheihhLGIsYyl7dGhpcy5hPWEKdGhpcy5iPWIKdGhpcy5jPWN9LAp2Vjpm
-dW5jdGlvbiB2VihhKXt0aGlzLmE9YX0sCnRlOmZ1bmN0aW9uIHRlKGEpe3RoaXMuYT1hfSwKYnE6ZnVu
-Y3Rpb24gYnEoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sClhPOmZ1bmN0aW9uIFhPKGEpe3RoaXMuYT1h
-CnRoaXMuYj1udWxsfSwKVHA6ZnVuY3Rpb24gVHAoKXt9LApsYzpmdW5jdGlvbiBsYygpe30sCnp4OmZ1
-bmN0aW9uIHp4KCl7fSwKclQ6ZnVuY3Rpb24gclQoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sCkVxOmZ1
-bmN0aW9uIEVxKGEpe3RoaXMuYT1hfSwKa1k6ZnVuY3Rpb24ga1koYSl7dGhpcy5hPWF9LAprcjpmdW5j
-dGlvbiBrcigpe30sCk41OmZ1bmN0aW9uIE41KGEpe3ZhciBfPXRoaXMKXy5hPTAKXy5mPV8uZT1fLmQ9
-Xy5jPV8uYj1udWxsCl8ucj0wCl8uJHRpPWF9LAp2aDpmdW5jdGlvbiB2aChhLGIpe3ZhciBfPXRoaXMK
-Xy5hPWEKXy5iPWIKXy5kPV8uYz1udWxsfSwKaTU6ZnVuY3Rpb24gaTUoYSxiKXt0aGlzLmE9YQp0aGlz
-LiR0aT1ifSwKTjY6ZnVuY3Rpb24gTjYoYSxiLGMpe3ZhciBfPXRoaXMKXy5hPWEKXy5iPWIKXy5kPV8u
-Yz1udWxsCl8uJHRpPWN9LApkQzpmdW5jdGlvbiBkQyhhKXt0aGlzLmE9YX0sCndOOmZ1bmN0aW9uIHdO
-KGEpe3RoaXMuYT1hfSwKVlg6ZnVuY3Rpb24gVlgoYSl7dGhpcy5hPWF9LApWUjpmdW5jdGlvbiBWUihh
-LGIpe3ZhciBfPXRoaXMKXy5hPWEKXy5iPWIKXy5kPV8uYz1udWxsfSwKRUs6ZnVuY3Rpb24gRUsoYSl7
-dGhpcy5iPWF9LApLVzpmdW5jdGlvbiBLVyhhLGIsYyl7dGhpcy5hPWEKdGhpcy5iPWIKdGhpcy5jPWN9
-LApQYjpmdW5jdGlvbiBQYihhLGIsYyl7dmFyIF89dGhpcwpfLmE9YQpfLmI9YgpfLmM9YwpfLmQ9bnVs
-bH0sCnRROmZ1bmN0aW9uIHRRKGEsYil7dGhpcy5hPWEKdGhpcy5jPWJ9LAp1bjpmdW5jdGlvbiB1bihh
-LGIsYyl7dGhpcy5hPWEKdGhpcy5iPWIKdGhpcy5jPWN9LApTZDpmdW5jdGlvbiBTZChhLGIsYyl7dmFy
-IF89dGhpcwpfLmE9YQpfLmI9YgpfLmM9YwpfLmQ9bnVsbH0sClhGOmZ1bmN0aW9uKGEpe3JldHVybiBh
-fSwKb2Q6ZnVuY3Rpb24oYSxiLGMpe2lmKGE+Pj4wIT09YXx8YT49Yyl0aHJvdyBILmIoSC51KGIsYSkp
-fSwKck06ZnVuY3Rpb24oYSxiLGMpe3ZhciBzCmlmKCEoYT4+PjAhPT1hKSlzPWI+Pj4wIT09Ynx8YT5i
-fHxiPmMKZWxzZSBzPSEwCmlmKHMpdGhyb3cgSC5iKEguRHUoYSxiLGMpKQpyZXR1cm4gYn0sCkVUOmZ1
-bmN0aW9uIEVUKCl7fSwKTFo6ZnVuY3Rpb24gTFooKXt9LApEZzpmdW5jdGlvbiBEZygpe30sClBnOmZ1
-bmN0aW9uIFBnKCl7fSwKeGo6ZnVuY3Rpb24geGooKXt9LApkRTpmdW5jdGlvbiBkRSgpe30sClpBOmZ1
-bmN0aW9uIFpBKCl7fSwKZFQ6ZnVuY3Rpb24gZFQoKXt9LApQcTpmdW5jdGlvbiBQcSgpe30sCmVFOmZ1
-bmN0aW9uIGVFKCl7fSwKVjY6ZnVuY3Rpb24gVjYoKXt9LApSRzpmdW5jdGlvbiBSRygpe30sClZQOmZ1
-bmN0aW9uIFZQKCl7fSwKV0I6ZnVuY3Rpb24gV0IoKXt9LApaRzpmdW5jdGlvbiBaRygpe30sCmN6OmZ1
-bmN0aW9uKGEsYil7dmFyIHM9Yi5jCnJldHVybiBzPT1udWxsP2IuYz1ILkIoYSxiLnosITApOnN9LAp4
-WjpmdW5jdGlvbihhLGIpe3ZhciBzPWIuYwpyZXR1cm4gcz09bnVsbD9iLmM9SC5KKGEsImI4IixbYi56
-XSk6c30sClExOmZ1bmN0aW9uKGEpe3ZhciBzPWEueQppZihzPT09Nnx8cz09PTd8fHM9PT04KXJldHVy
-biBILlExKGEueikKcmV0dXJuIHM9PT0xMXx8cz09PTEyfSwKbUQ6ZnVuY3Rpb24oYSl7cmV0dXJuIGEu
-Y3l9LApOMDpmdW5jdGlvbihhKXtyZXR1cm4gSC5FKHYudHlwZVVuaXZlcnNlLGEsITEpfSwKUEw6ZnVu
-Y3Rpb24oYSxiLGEwLGExKXt2YXIgcyxyLHEscCxvLG4sbSxsLGssaixpLGgsZyxmLGUsZCxjPWIueQpz
-d2l0Y2goYyl7Y2FzZSA1OmNhc2UgMTpjYXNlIDI6Y2FzZSAzOmNhc2UgNDpyZXR1cm4gYgpjYXNlIDY6
-cz1iLnoKcj1ILlBMKGEscyxhMCxhMSkKaWYocj09PXMpcmV0dXJuIGIKcmV0dXJuIEguQyhhLHIsITAp
-CmNhc2UgNzpzPWIuegpyPUguUEwoYSxzLGEwLGExKQppZihyPT09cylyZXR1cm4gYgpyZXR1cm4gSC5C
-KGEsciwhMCkKY2FzZSA4OnM9Yi56CnI9SC5QTChhLHMsYTAsYTEpCmlmKHI9PT1zKXJldHVybiBiCnJl
-dHVybiBILmYoYSxyLCEwKQpjYXNlIDk6cT1iLlEKcD1ILmJaKGEscSxhMCxhMSkKaWYocD09PXEpcmV0
-dXJuIGIKcmV0dXJuIEguSihhLGIueixwKQpjYXNlIDEwOm89Yi56Cm49SC5QTChhLG8sYTAsYTEpCm09
-Yi5RCmw9SC5iWihhLG0sYTAsYTEpCmlmKG49PT1vJiZsPT09bSlyZXR1cm4gYgpyZXR1cm4gSC5hKGEs
-bixsKQpjYXNlIDExOms9Yi56Cmo9SC5QTChhLGssYTAsYTEpCmk9Yi5RCmg9SC5xVChhLGksYTAsYTEp
-CmlmKGo9PT1rJiZoPT09aSlyZXR1cm4gYgpyZXR1cm4gSC5kKGEsaixoKQpjYXNlIDEyOmc9Yi5RCmEx
-Kz1nLmxlbmd0aApmPUguYlooYSxnLGEwLGExKQpvPWIuegpuPUguUEwoYSxvLGEwLGExKQppZihmPT09
-ZyYmbj09PW8pcmV0dXJuIGIKcmV0dXJuIEguRChhLG4sZiwhMCkKY2FzZSAxMzplPWIuegppZihlPGEx
-KXJldHVybiBiCmQ9YTBbZS1hMV0KaWYoZD09bnVsbClyZXR1cm4gYgpyZXR1cm4gZApkZWZhdWx0OnRo
-cm93IEguYihQLmhWKCJBdHRlbXB0ZWQgdG8gc3Vic3RpdHV0ZSB1bmV4cGVjdGVkIFJUSSBraW5kICIr
-YykpfX0sCmJaOmZ1bmN0aW9uKGEsYixjLGQpe3ZhciBzLHIscSxwLG89Yi5sZW5ndGgsbj1bXQpmb3Io
-cz0hMSxyPTA7cjxvOysrcil7cT1iW3JdCnA9SC5QTChhLHEsYyxkKQppZihwIT09cSlzPSEwCm4ucHVz
-aChwKX1yZXR1cm4gcz9uOmJ9LAp2TzpmdW5jdGlvbihhLGIsYyxkKXt2YXIgcyxyLHEscCxvLG4sbT1i
-Lmxlbmd0aCxsPVtdCmZvcihzPSExLHI9MDtyPG07cis9Myl7cT1iW3JdCnA9YltyKzFdCm89YltyKzJd
-Cm49SC5QTChhLG8sYyxkKQppZihuIT09bylzPSEwCmwucHVzaChxKQpsLnB1c2gocCkKbC5wdXNoKG4p
-fXJldHVybiBzP2w6Yn0sCnFUOmZ1bmN0aW9uKGEsYixjLGQpe3ZhciBzLHI9Yi5hLHE9SC5iWihhLHIs
-YyxkKSxwPWIuYixvPUguYlooYSxwLGMsZCksbj1iLmMsbT1ILnZPKGEsbixjLGQpCmlmKHE9PT1yJiZv
-PT09cCYmbT09PW4pcmV0dXJuIGIKcz1uZXcgSC5HKCkKcy5hPXEKcy5iPW8Kcy5jPW0KcmV0dXJuIHN9
-LApRSTpmdW5jdGlvbihhLGIpe2Fbdi5hcnJheVJ0aV09YgpyZXR1cm4gYX0sCkpTOmZ1bmN0aW9uKGEp
-e3ZhciBzPWEuJFMKaWYocyE9bnVsbCl7aWYodHlwZW9mIHM9PSJudW1iZXIiKXJldHVybiBILkJwKHMp
-CnJldHVybiBhLiRTKCl9cmV0dXJuIG51bGx9LApVZTpmdW5jdGlvbihhLGIpe3ZhciBzCmlmKEguUTEo
-YikpaWYoYSBpbnN0YW5jZW9mIEguVHApe3M9SC5KUyhhKQppZihzIT1udWxsKXJldHVybiBzfXJldHVy
-biBILnpLKGEpfSwKeks6ZnVuY3Rpb24oYSl7dmFyIHMKaWYoYSBpbnN0YW5jZW9mIFAuTWgpe3M9YS4k
-dGkKcmV0dXJuIHMhPW51bGw/czpILlZVKGEpfWlmKEFycmF5LmlzQXJyYXkoYSkpcmV0dXJuIEgudDYo
-YSkKcmV0dXJuIEguVlUoSi5pYShhKSl9LAp0NjpmdW5jdGlvbihhKXt2YXIgcz1hW3YuYXJyYXlSdGld
-LHI9dC5iCmlmKHM9PW51bGwpcmV0dXJuIHIKaWYocy5jb25zdHJ1Y3RvciE9PXIuY29uc3RydWN0b3Ip
-cmV0dXJuIHIKcmV0dXJuIHN9LApMaDpmdW5jdGlvbihhKXt2YXIgcz1hLiR0aQpyZXR1cm4gcyE9bnVs
-bD9zOkguVlUoYSl9LApWVTpmdW5jdGlvbihhKXt2YXIgcz1hLmNvbnN0cnVjdG9yLHI9cy4kY2NhY2hl
-CmlmKHIhPW51bGwpcmV0dXJuIHIKcmV0dXJuIEgucjkoYSxzKX0sCnI5OmZ1bmN0aW9uKGEsYil7dmFy
-IHM9YSBpbnN0YW5jZW9mIEguVHA/YS5fX3Byb3RvX18uX19wcm90b19fLmNvbnN0cnVjdG9yOmIscj1I
-LmFpKHYudHlwZVVuaXZlcnNlLHMubmFtZSkKYi4kY2NhY2hlPXIKcmV0dXJuIHJ9LApCcDpmdW5jdGlv
-bihhKXt2YXIgcyxyLHEKSC51UChhKQpzPXYudHlwZXMKcj1zW2FdCmlmKHR5cGVvZiByPT0ic3RyaW5n
-Iil7cT1ILkUodi50eXBlVW5pdmVyc2UsciwhMSkKc1thXT1xCnJldHVybiBxfXJldHVybiByfSwKS3g6
-ZnVuY3Rpb24oYSl7dmFyIHMscixxLHA9YS54CmlmKHAhPW51bGwpcmV0dXJuIHAKcz1hLmN5CnI9cy5y
-ZXBsYWNlKC9cKi9nLCIiKQppZihyPT09cylyZXR1cm4gYS54PW5ldyBILmxZKGEpCnE9SC5FKHYudHlw
-ZVVuaXZlcnNlLHIsITApCnA9cS54CnJldHVybiBhLng9cD09bnVsbD9xLng9bmV3IEgubFkocSk6cH0s
-CkpKOmZ1bmN0aW9uKGEpe3ZhciBzLHIscSxwPXRoaXMsbz10LksKaWYocD09PW8pcmV0dXJuIEguUkUo
-cCxhLEgua2UpCmlmKCFILkE4KHApKWlmKCEocD09PXQuXykpbz1wPT09bwplbHNlIG89ITAKZWxzZSBv
-PSEwCmlmKG8pcmV0dXJuIEguUkUocCxhLEguSXcpCm89cC55CnM9bz09PTY/cC56OnAKaWYocz09PXQu
-UylyPUgub2sKZWxzZSBpZihzPT09dC5nUnx8cz09PXQuZGkpcj1ILktICmVsc2UgaWYocz09PXQuTily
-PUguTU0KZWxzZSByPXM9PT10Lnk/SC5yUTpudWxsCmlmKHIhPW51bGwpcmV0dXJuIEguUkUocCxhLHIp
-CmlmKHMueT09PTkpe3E9cy56CmlmKHMuUS5ldmVyeShILmNjKSl7cC5yPSIkaSIrcQppZihxPT09InpN
-IilyZXR1cm4gSC5SRShwLGEsSC55TSkKcmV0dXJuIEguUkUocCxhLEgudDQpfX1lbHNlIGlmKG89PT03
-KXJldHVybiBILlJFKHAsYSxILkFRKQpyZXR1cm4gSC5SRShwLGEsSC5ZTyl9LApSRTpmdW5jdGlvbihh
-LGIsYyl7YS5iPWMKcmV0dXJuIGEuYihiKX0sCkF1OmZ1bmN0aW9uKGEpe3ZhciBzLHIscT10aGlzCmlm
-KCFILkE4KHEpKWlmKCEocT09PXQuXykpcz1xPT09dC5LCmVsc2Ugcz0hMAplbHNlIHM9ITAKaWYocyly
-PUguaG4KZWxzZSBpZihxPT09dC5LKXI9SC5UaQplbHNlIHI9SC5sNApxLmE9cgpyZXR1cm4gcS5hKGEp
-fSwKUWo6ZnVuY3Rpb24oYSl7dmFyIHMscj1hLnkKaWYoIUguQTgoYSkpaWYoIShhPT09dC5fKSlpZigh
-KGE9PT10LmNGKSlpZihyIT09NylzPXI9PT04JiZILlFqKGEueil8fGE9PT10LlB8fGE9PT10LlQKZWxz
-ZSBzPSEwCmVsc2Ugcz0hMAplbHNlIHM9ITAKZWxzZSBzPSEwCnJldHVybiBzfSwKWU86ZnVuY3Rpb24o
-YSl7dmFyIHM9dGhpcwppZihhPT1udWxsKXJldHVybiBILlFqKHMpCnJldHVybiBILldlKHYudHlwZVVu
-aXZlcnNlLEguVWUoYSxzKSxudWxsLHMsbnVsbCl9LApBUTpmdW5jdGlvbihhKXtpZihhPT1udWxsKXJl
-dHVybiEwCnJldHVybiB0aGlzLnouYihhKX0sCnQ0OmZ1bmN0aW9uKGEpe3ZhciBzLHI9dGhpcwppZihh
-PT1udWxsKXJldHVybiBILlFqKHIpCnM9ci5yCmlmKGEgaW5zdGFuY2VvZiBQLk1oKXJldHVybiEhYVtz
-XQpyZXR1cm4hIUouaWEoYSlbc119LAp5TTpmdW5jdGlvbihhKXt2YXIgcyxyPXRoaXMKaWYoYT09bnVs
-bClyZXR1cm4gSC5RaihyKQppZih0eXBlb2YgYSE9Im9iamVjdCIpcmV0dXJuITEKaWYoQXJyYXkuaXNB
-cnJheShhKSlyZXR1cm4hMApzPXIucgppZihhIGluc3RhbmNlb2YgUC5NaClyZXR1cm4hIWFbc10KcmV0
-dXJuISFKLmlhKGEpW3NdfSwKT3o6ZnVuY3Rpb24oYSl7dmFyIHM9dGhpcwppZihhPT1udWxsKXJldHVy
-biBhCmVsc2UgaWYocy5iKGEpKXJldHVybiBhCkgubTQoYSxzKX0sCmw0OmZ1bmN0aW9uKGEpe3ZhciBz
-PXRoaXMKaWYoYT09bnVsbClyZXR1cm4gYQplbHNlIGlmKHMuYihhKSlyZXR1cm4gYQpILm00KGEscyl9
-LAptNDpmdW5jdGlvbihhLGIpe3Rocm93IEguYihILlpjKEguV0soYSxILlVlKGEsYiksSC5kbShiLG51
-bGwpKSkpfSwKRGg6ZnVuY3Rpb24oYSxiLGMsZCl7dmFyIHM9bnVsbAppZihILldlKHYudHlwZVVuaXZl
-cnNlLGEscyxiLHMpKXJldHVybiBhCnRocm93IEguYihILlpjKCJUaGUgdHlwZSBhcmd1bWVudCAnIitI
-LkVqKEguZG0oYSxzKSkrIicgaXMgbm90IGEgc3VidHlwZSBvZiB0aGUgdHlwZSB2YXJpYWJsZSBib3Vu
-ZCAnIitILkVqKEguZG0oYixzKSkrIicgb2YgdHlwZSB2YXJpYWJsZSAnIitILkVqKGMpKyInIGluICci
-K0guRWooZCkrIicuIikpfSwKV0s6ZnVuY3Rpb24oYSxiLGMpe3ZhciBzPVAuaGwoYSkscj1ILmRtKGI9
-PW51bGw/SC56SyhhKTpiLG51bGwpCnJldHVybiBzKyI6IHR5cGUgJyIrSC5FaihyKSsiJyBpcyBub3Qg
-YSBzdWJ0eXBlIG9mIHR5cGUgJyIrSC5FaihjKSsiJyJ9LApaYzpmdW5jdGlvbihhKXtyZXR1cm4gbmV3
-IEguaU0oIlR5cGVFcnJvcjogIithKX0sCnE6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gbmV3IEguaU0oIlR5
-cGVFcnJvcjogIitILldLKGEsbnVsbCxiKSl9LAprZTpmdW5jdGlvbihhKXtyZXR1cm4gYSE9bnVsbH0s
-ClRpOmZ1bmN0aW9uKGEpe3JldHVybiBhfSwKSXc6ZnVuY3Rpb24oYSl7cmV0dXJuITB9LApobjpmdW5j
-dGlvbihhKXtyZXR1cm4gYX0sCnJROmZ1bmN0aW9uKGEpe3JldHVybiEwPT09YXx8ITE9PT1hfSwKcDg6
-ZnVuY3Rpb24oYSl7aWYoITA9PT1hKXJldHVybiEwCmlmKCExPT09YSlyZXR1cm4hMQp0aHJvdyBILmIo
-SC5xKGEsImJvb2wiKSl9LAp5ODpmdW5jdGlvbihhKXtpZighMD09PWEpcmV0dXJuITAKaWYoITE9PT1h
-KXJldHVybiExCmlmKGE9PW51bGwpcmV0dXJuIGEKdGhyb3cgSC5iKEgucShhLCJib29sIikpfSwKZHA6
-ZnVuY3Rpb24oYSl7aWYoITA9PT1hKXJldHVybiEwCmlmKCExPT09YSlyZXR1cm4hMQppZihhPT1udWxs
-KXJldHVybiBhCnRocm93IEguYihILnEoYSwiYm9vbD8iKSl9LApGRzpmdW5jdGlvbihhKXtpZih0eXBl
-b2YgYT09Im51bWJlciIpcmV0dXJuIGEKdGhyb3cgSC5iKEgucShhLCJkb3VibGUiKSl9LApHSDpmdW5j
-dGlvbihhKXtpZih0eXBlb2YgYT09Im51bWJlciIpcmV0dXJuIGEKaWYoYT09bnVsbClyZXR1cm4gYQp0
-aHJvdyBILmIoSC5xKGEsImRvdWJsZSIpKX0sClFrOmZ1bmN0aW9uKGEpe2lmKHR5cGVvZiBhPT0ibnVt
-YmVyIilyZXR1cm4gYQppZihhPT1udWxsKXJldHVybiBhCnRocm93IEguYihILnEoYSwiZG91YmxlPyIp
-KX0sCm9rOmZ1bmN0aW9uKGEpe3JldHVybiB0eXBlb2YgYT09Im51bWJlciImJk1hdGguZmxvb3IoYSk9
-PT1hfSwKSVo6ZnVuY3Rpb24oYSl7aWYodHlwZW9mIGE9PSJudW1iZXIiJiZNYXRoLmZsb29yKGEpPT09
-YSlyZXR1cm4gYQp0aHJvdyBILmIoSC5xKGEsImludCIpKX0sCnVQOmZ1bmN0aW9uKGEpe2lmKHR5cGVv
-ZiBhPT0ibnVtYmVyIiYmTWF0aC5mbG9vcihhKT09PWEpcmV0dXJuIGEKaWYoYT09bnVsbClyZXR1cm4g
-YQp0aHJvdyBILmIoSC5xKGEsImludCIpKX0sClVjOmZ1bmN0aW9uKGEpe2lmKHR5cGVvZiBhPT0ibnVt
-YmVyIiYmTWF0aC5mbG9vcihhKT09PWEpcmV0dXJuIGEKaWYoYT09bnVsbClyZXR1cm4gYQp0aHJvdyBI
-LmIoSC5xKGEsImludD8iKSl9LApLSDpmdW5jdGlvbihhKXtyZXR1cm4gdHlwZW9mIGE9PSJudW1iZXIi
-fSwKejU6ZnVuY3Rpb24oYSl7aWYodHlwZW9mIGE9PSJudW1iZXIiKXJldHVybiBhCnRocm93IEguYihI
-LnEoYSwibnVtIikpfSwKVzE6ZnVuY3Rpb24oYSl7aWYodHlwZW9mIGE9PSJudW1iZXIiKXJldHVybiBh
-CmlmKGE9PW51bGwpcmV0dXJuIGEKdGhyb3cgSC5iKEgucShhLCJudW0iKSl9LApjVTpmdW5jdGlvbihh
-KXtpZih0eXBlb2YgYT09Im51bWJlciIpcmV0dXJuIGEKaWYoYT09bnVsbClyZXR1cm4gYQp0aHJvdyBI
-LmIoSC5xKGEsIm51bT8iKSl9LApNTTpmdW5jdGlvbihhKXtyZXR1cm4gdHlwZW9mIGE9PSJzdHJpbmci
-fSwKQnQ6ZnVuY3Rpb24oYSl7aWYodHlwZW9mIGE9PSJzdHJpbmciKXJldHVybiBhCnRocm93IEguYihI
-LnEoYSwiU3RyaW5nIikpfSwKaDpmdW5jdGlvbihhKXtpZih0eXBlb2YgYT09InN0cmluZyIpcmV0dXJu
-IGEKaWYoYT09bnVsbClyZXR1cm4gYQp0aHJvdyBILmIoSC5xKGEsIlN0cmluZyIpKX0sCms6ZnVuY3Rp
+b2ZmIil9LAp2cTpmdW5jdGlvbihhLGIsYyxkKXt2YXIgcz1ILkRWCnN3aXRjaChiPy0xOmEpe2Nhc2Ug
+MDpyZXR1cm4gZnVuY3Rpb24oZSxmKXtyZXR1cm4gZnVuY3Rpb24oKXtyZXR1cm4gZih0aGlzKVtlXSgp
+fX0oYyxzKQpjYXNlIDE6cmV0dXJuIGZ1bmN0aW9uKGUsZil7cmV0dXJuIGZ1bmN0aW9uKGcpe3JldHVy
+biBmKHRoaXMpW2VdKGcpfX0oYyxzKQpjYXNlIDI6cmV0dXJuIGZ1bmN0aW9uKGUsZil7cmV0dXJuIGZ1
+bmN0aW9uKGcsaCl7cmV0dXJuIGYodGhpcylbZV0oZyxoKX19KGMscykKY2FzZSAzOnJldHVybiBmdW5j
+dGlvbihlLGYpe3JldHVybiBmdW5jdGlvbihnLGgsaSl7cmV0dXJuIGYodGhpcylbZV0oZyxoLGkpfX0o
+YyxzKQpjYXNlIDQ6cmV0dXJuIGZ1bmN0aW9uKGUsZil7cmV0dXJuIGZ1bmN0aW9uKGcsaCxpLGope3Jl
+dHVybiBmKHRoaXMpW2VdKGcsaCxpLGopfX0oYyxzKQpjYXNlIDU6cmV0dXJuIGZ1bmN0aW9uKGUsZil7
+cmV0dXJuIGZ1bmN0aW9uKGcsaCxpLGosayl7cmV0dXJuIGYodGhpcylbZV0oZyxoLGksaixrKX19KGMs
+cykKZGVmYXVsdDpyZXR1cm4gZnVuY3Rpb24oZSxmKXtyZXR1cm4gZnVuY3Rpb24oKXtyZXR1cm4gZS5h
+cHBseShmKHRoaXMpLGFyZ3VtZW50cyl9fShkLHMpfX0sCmJ4OmZ1bmN0aW9uKGEsYixjLGQpe3ZhciBz
+LHIscSxwLG8KaWYoYylyZXR1cm4gSC5IZihhLGIsZCkKcz1iLmxlbmd0aApyPWR8fHM+PTI3CmlmKHIp
+cmV0dXJuIEgudnEocyxkLGEsYikKaWYocz09PTApe3I9JC55agppZih0eXBlb2YgciE9PSJudW1iZXIi
+KXJldHVybiByLmgoKQokLnlqPXIrMQpxPSJzZWxmIityCnI9InJldHVybiBmdW5jdGlvbigpe3ZhciAi
+K3ErIiA9IHRoaXMuIgpwPSQuV1cKcmV0dXJuIG5ldyBGdW5jdGlvbihyKyhwPT1udWxsPyQuV1c9SC5t
+OSgic2VsZiIpOnApKyI7cmV0dXJuICIrcSsiLiIrYSsiKCk7fSIpKCl9bz0iYWJjZGVmZ2hpamtsbW5v
+cHFyc3R1dnd4eXoiLnNwbGl0KCIiKS5zcGxpY2UoMCxzKS5qb2luKCIsIikKcj0kLnlqCmlmKHR5cGVv
+ZiByIT09Im51bWJlciIpcmV0dXJuIHIuaCgpCiQueWo9cisxCm8rPXIKcj0icmV0dXJuIGZ1bmN0aW9u
+KCIrbysiKXtyZXR1cm4gdGhpcy4iCnA9JC5XVwpyZXR1cm4gbmV3IEZ1bmN0aW9uKHIrKHA9PW51bGw/
+JC5XVz1ILm05KCJzZWxmIik6cCkrIi4iK2ErIigiK28rIik7fSIpKCl9LApaNDpmdW5jdGlvbihhLGIs
+YyxkKXt2YXIgcz1ILkRWLHI9SC55Uwpzd2l0Y2goYj8tMTphKXtjYXNlIDA6dGhyb3cgSC5iKG5ldyBI
+LkVxKCJJbnRlcmNlcHRlZCBmdW5jdGlvbiB3aXRoIG5vIGFyZ3VtZW50cy4iKSkKY2FzZSAxOnJldHVy
+biBmdW5jdGlvbihlLGYsZyl7cmV0dXJuIGZ1bmN0aW9uKCl7cmV0dXJuIGYodGhpcylbZV0oZyh0aGlz
+KSl9fShjLHMscikKY2FzZSAyOnJldHVybiBmdW5jdGlvbihlLGYsZyl7cmV0dXJuIGZ1bmN0aW9uKGgp
+e3JldHVybiBmKHRoaXMpW2VdKGcodGhpcyksaCl9fShjLHMscikKY2FzZSAzOnJldHVybiBmdW5jdGlv
+bihlLGYsZyl7cmV0dXJuIGZ1bmN0aW9uKGgsaSl7cmV0dXJuIGYodGhpcylbZV0oZyh0aGlzKSxoLGkp
+fX0oYyxzLHIpCmNhc2UgNDpyZXR1cm4gZnVuY3Rpb24oZSxmLGcpe3JldHVybiBmdW5jdGlvbihoLGks
+ail7cmV0dXJuIGYodGhpcylbZV0oZyh0aGlzKSxoLGksail9fShjLHMscikKY2FzZSA1OnJldHVybiBm
+dW5jdGlvbihlLGYsZyl7cmV0dXJuIGZ1bmN0aW9uKGgsaSxqLGspe3JldHVybiBmKHRoaXMpW2VdKGco
+dGhpcyksaCxpLGosayl9fShjLHMscikKY2FzZSA2OnJldHVybiBmdW5jdGlvbihlLGYsZyl7cmV0dXJu
+IGZ1bmN0aW9uKGgsaSxqLGssbCl7cmV0dXJuIGYodGhpcylbZV0oZyh0aGlzKSxoLGksaixrLGwpfX0o
+YyxzLHIpCmRlZmF1bHQ6cmV0dXJuIGZ1bmN0aW9uKGUsZixnLGgpe3JldHVybiBmdW5jdGlvbigpe2g9
+W2codGhpcyldCkFycmF5LnByb3RvdHlwZS5wdXNoLmFwcGx5KGgsYXJndW1lbnRzKQpyZXR1cm4gZS5h
+cHBseShmKHRoaXMpLGgpfX0oZCxzLHIpfX0sCkhmOmZ1bmN0aW9uKGEsYixjKXt2YXIgcyxyLHEscCxv
+LG49JC5XVwppZihuPT1udWxsKW49JC5XVz1ILm05KCJzZWxmIikKcz0kLmkwCmlmKHM9PW51bGwpcz0k
+LmkwPUgubTkoInJlY2VpdmVyIikKcj1iLmxlbmd0aApxPWN8fHI+PTI4CmlmKHEpcmV0dXJuIEguWjQo
+cixjLGEsYikKaWYocj09PTEpe3E9InJldHVybiBmdW5jdGlvbigpe3JldHVybiB0aGlzLiIrbisiLiIr
+YSsiKHRoaXMuIitzKyIpOyIKcD0kLnlqCmlmKHR5cGVvZiBwIT09Im51bWJlciIpcmV0dXJuIHAuaCgp
+CiQueWo9cCsxCnJldHVybiBuZXcgRnVuY3Rpb24ocStwKyJ9IikoKX1vPSJhYmNkZWZnaGlqa2xtbm9w
+cXJzdHV2d3h5eiIuc3BsaXQoIiIpLnNwbGljZSgwLHItMSkuam9pbigiLCIpCnE9InJldHVybiBmdW5j
+dGlvbigiK28rIil7cmV0dXJuIHRoaXMuIituKyIuIithKyIodGhpcy4iK3MrIiwgIitvKyIpOyIKcD0k
+LnlqCmlmKHR5cGVvZiBwIT09Im51bWJlciIpcmV0dXJuIHAuaCgpCiQueWo9cCsxCnJldHVybiBuZXcg
+RnVuY3Rpb24ocStwKyJ9IikoKX0sClUyOmZ1bmN0aW9uKGEpe3JldHVybiBILmlBKGEpfSwKVG46ZnVu
+Y3Rpb24oYSxiKXtyZXR1cm4gSC5jRSh2LnR5cGVVbml2ZXJzZSxILnpLKGEuYSksYil9LApQVzpmdW5j
+dGlvbihhLGIpe3JldHVybiBILmNFKHYudHlwZVVuaXZlcnNlLEgueksoYS5iKSxiKX0sCkRWOmZ1bmN0
+aW9uKGEpe3JldHVybiBhLmF9LAp5UzpmdW5jdGlvbihhKXtyZXR1cm4gYS5ifSwKbTk6ZnVuY3Rpb24o
+YSl7dmFyIHMscixxLHA9bmV3IEguclQoInNlbGYiLCJyZWNlaXZlciIpLG89Si5FcChPYmplY3QuZ2V0
+T3duUHJvcGVydHlOYW1lcyhwKSx0LlgpCmZvcihzPW8ubGVuZ3RoLHI9MDtyPHM7KytyKXtxPW9bcl0K
+aWYocFtxXT09PWEpcmV0dXJuIHF9dGhyb3cgSC5iKFAueFkoIkZpZWxkIG5hbWUgIithKyIgbm90IGZv
+dW5kLiIpKX0sCm9UOmZ1bmN0aW9uKGEpe2lmKGE9PW51bGwpSC5mTygiYm9vbGVhbiBleHByZXNzaW9u
+IG11c3Qgbm90IGJlIG51bGwiKQpyZXR1cm4gYX0sCmZPOmZ1bmN0aW9uKGEpe3Rocm93IEguYihuZXcg
+SC5rWShhKSl9LAphZzpmdW5jdGlvbihhKXt0aHJvdyBILmIobmV3IFAucChhKSl9LApZZzpmdW5jdGlv
+bihhKXtyZXR1cm4gdi5nZXRJc29sYXRlVGFnKGEpfSwKaXc6ZnVuY3Rpb24oYSxiLGMpe09iamVjdC5k
+ZWZpbmVQcm9wZXJ0eShhLGIse3ZhbHVlOmMsZW51bWVyYWJsZTpmYWxzZSx3cml0YWJsZTp0cnVlLGNv
+bmZpZ3VyYWJsZTp0cnVlfSl9LAp3MzpmdW5jdGlvbihhKXt2YXIgcyxyLHEscCxvLG49SC5uKCQuTkYu
+JDEoYSkpLG09JC5ud1tuXQppZihtIT1udWxsKXtPYmplY3QuZGVmaW5lUHJvcGVydHkoYSx2LmRpc3Bh
+dGNoUHJvcGVydHlOYW1lLHt2YWx1ZTptLGVudW1lcmFibGU6ZmFsc2Usd3JpdGFibGU6dHJ1ZSxjb25m
+aWd1cmFibGU6dHJ1ZX0pCnJldHVybiBtLml9cz0kLnZ2W25dCmlmKHMhPW51bGwpcmV0dXJuIHMKcj12
+LmludGVyY2VwdG9yc0J5VGFnW25dCmlmKHI9PW51bGwpe3E9SC5rKCQuVFguJDIoYSxuKSkKaWYocSE9
+bnVsbCl7bT0kLm53W3FdCmlmKG0hPW51bGwpe09iamVjdC5kZWZpbmVQcm9wZXJ0eShhLHYuZGlzcGF0
+Y2hQcm9wZXJ0eU5hbWUse3ZhbHVlOm0sZW51bWVyYWJsZTpmYWxzZSx3cml0YWJsZTp0cnVlLGNvbmZp
+Z3VyYWJsZTp0cnVlfSkKcmV0dXJuIG0uaX1zPSQudnZbcV0KaWYocyE9bnVsbClyZXR1cm4gcwpyPXYu
+aW50ZXJjZXB0b3JzQnlUYWdbcV0Kbj1xfX1pZihyPT1udWxsKXJldHVybiBudWxsCnM9ci5wcm90b3R5
+cGUKcD1uWzBdCmlmKHA9PT0iISIpe209SC5WYShzKQokLm53W25dPW0KT2JqZWN0LmRlZmluZVByb3Bl
+cnR5KGEsdi5kaXNwYXRjaFByb3BlcnR5TmFtZSx7dmFsdWU6bSxlbnVtZXJhYmxlOmZhbHNlLHdyaXRh
+YmxlOnRydWUsY29uZmlndXJhYmxlOnRydWV9KQpyZXR1cm4gbS5pfWlmKHA9PT0ifiIpeyQudnZbbl09
+cwpyZXR1cm4gc31pZihwPT09Ii0iKXtvPUguVmEocykKT2JqZWN0LmRlZmluZVByb3BlcnR5KE9iamVj
+dC5nZXRQcm90b3R5cGVPZihhKSx2LmRpc3BhdGNoUHJvcGVydHlOYW1lLHt2YWx1ZTpvLGVudW1lcmFi
+bGU6ZmFsc2Usd3JpdGFibGU6dHJ1ZSxjb25maWd1cmFibGU6dHJ1ZX0pCnJldHVybiBvLml9aWYocD09
+PSIrIilyZXR1cm4gSC5MYyhhLHMpCmlmKHA9PT0iKiIpdGhyb3cgSC5iKFAuU1kobikpCmlmKHYubGVh
+ZlRhZ3Nbbl09PT10cnVlKXtvPUguVmEocykKT2JqZWN0LmRlZmluZVByb3BlcnR5KE9iamVjdC5nZXRQ
+cm90b3R5cGVPZihhKSx2LmRpc3BhdGNoUHJvcGVydHlOYW1lLHt2YWx1ZTpvLGVudW1lcmFibGU6ZmFs
+c2Usd3JpdGFibGU6dHJ1ZSxjb25maWd1cmFibGU6dHJ1ZX0pCnJldHVybiBvLml9ZWxzZSByZXR1cm4g
+SC5MYyhhLHMpfSwKTGM6ZnVuY3Rpb24oYSxiKXt2YXIgcz1PYmplY3QuZ2V0UHJvdG90eXBlT2YoYSkK
+T2JqZWN0LmRlZmluZVByb3BlcnR5KHMsdi5kaXNwYXRjaFByb3BlcnR5TmFtZSx7dmFsdWU6Si5RdShi
+LHMsbnVsbCxudWxsKSxlbnVtZXJhYmxlOmZhbHNlLHdyaXRhYmxlOnRydWUsY29uZmlndXJhYmxlOnRy
+dWV9KQpyZXR1cm4gYn0sClZhOmZ1bmN0aW9uKGEpe3JldHVybiBKLlF1KGEsITEsbnVsbCwhIWEuJGlY
+ail9LApWRjpmdW5jdGlvbihhLGIsYyl7dmFyIHM9Yi5wcm90b3R5cGUKaWYodi5sZWFmVGFnc1thXT09
+PXRydWUpcmV0dXJuIEguVmEocykKZWxzZSByZXR1cm4gSi5RdShzLGMsbnVsbCxudWxsKX0sClhEOmZ1
+bmN0aW9uKCl7aWYoITA9PT0kLkJ2KXJldHVybgokLkJ2PSEwCkguWjEoKX0sCloxOmZ1bmN0aW9uKCl7
+dmFyIHMscixxLHAsbyxuLG0sbAokLm53PU9iamVjdC5jcmVhdGUobnVsbCkKJC52dj1PYmplY3QuY3Jl
+YXRlKG51bGwpCkgua08oKQpzPXYuaW50ZXJjZXB0b3JzQnlUYWcKcj1PYmplY3QuZ2V0T3duUHJvcGVy
+dHlOYW1lcyhzKQppZih0eXBlb2Ygd2luZG93IT0idW5kZWZpbmVkIil7d2luZG93CnE9ZnVuY3Rpb24o
+KXt9CmZvcihwPTA7cDxyLmxlbmd0aDsrK3Ape289cltwXQpuPSQueDcuJDEobykKaWYobiE9bnVsbCl7
+bT1ILlZGKG8sc1tvXSxuKQppZihtIT1udWxsKXtPYmplY3QuZGVmaW5lUHJvcGVydHkobix2LmRpc3Bh
+dGNoUHJvcGVydHlOYW1lLHt2YWx1ZTptLGVudW1lcmFibGU6ZmFsc2Usd3JpdGFibGU6dHJ1ZSxjb25m
+aWd1cmFibGU6dHJ1ZX0pCnEucHJvdG90eXBlPW59fX19Zm9yKHA9MDtwPHIubGVuZ3RoOysrcCl7bz1y
+W3BdCmlmKC9eW0EtWmEtel9dLy50ZXN0KG8pKXtsPXNbb10Kc1siISIrb109bApzWyJ+IitvXT1sCnNb
+Ii0iK29dPWwKc1siKyIrb109bApzWyIqIitvXT1sfX19LAprTzpmdW5jdGlvbigpe3ZhciBzLHIscSxw
+LG8sbixtPUMuWXEoKQptPUgudWQoQy5LVSxILnVkKEMuZlEsSC51ZChDLmk3LEgudWQoQy5pNyxILnVk
+KEMueGksSC51ZChDLmRrLEgudWQoQy53YihDLk80KSxtKSkpKSkpKQppZih0eXBlb2YgZGFydE5hdGl2
+ZURpc3BhdGNoSG9va3NUcmFuc2Zvcm1lciE9InVuZGVmaW5lZCIpe3M9ZGFydE5hdGl2ZURpc3BhdGNo
+SG9va3NUcmFuc2Zvcm1lcgppZih0eXBlb2Ygcz09ImZ1bmN0aW9uIilzPVtzXQppZihzLmNvbnN0cnVj
+dG9yPT1BcnJheSlmb3Iocj0wO3I8cy5sZW5ndGg7KytyKXtxPXNbcl0KaWYodHlwZW9mIHE9PSJmdW5j
+dGlvbiIpbT1xKG0pfHxtfX1wPW0uZ2V0VGFnCm89bS5nZXRVbmtub3duVGFnCm49bS5wcm90b3R5cGVG
+b3JUYWcKJC5ORj1uZXcgSC5kQyhwKQokLlRYPW5ldyBILndOKG8pCiQueDc9bmV3IEguVlgobil9LAp1
+ZDpmdW5jdGlvbihhLGIpe3JldHVybiBhKGIpfHxifSwKdjQ6ZnVuY3Rpb24oYSxiLGMsZCxlLGYpe3Zh
+ciBzPWI/Im0iOiIiLHI9Yz8iIjoiaSIscT1kPyJ1IjoiIixwPWU/InMiOiIiLG89Zj8iZyI6IiIsbj1m
+dW5jdGlvbihnLGgpe3RyeXtyZXR1cm4gbmV3IFJlZ0V4cChnLGgpfWNhdGNoKG0pe3JldHVybiBtfX0o
+YSxzK3IrcStwK28pCmlmKG4gaW5zdGFuY2VvZiBSZWdFeHApcmV0dXJuIG4KdGhyb3cgSC5iKFAucnIo
+IklsbGVnYWwgUmVnRXhwIHBhdHRlcm4gKCIrU3RyaW5nKG4pKyIpIixhLG51bGwpKX0sClNROmZ1bmN0
+aW9uKGEsYixjKXt2YXIgcwppZih0eXBlb2YgYj09InN0cmluZyIpcmV0dXJuIGEuaW5kZXhPZihiLGMp
+Pj0wCmVsc2UgaWYoYiBpbnN0YW5jZW9mIEguVlIpe3M9Qy54Qi55bihhLGMpCnJldHVybiBiLmIudGVz
+dChzKX1lbHNle3M9Si5GTChiLEMueEIueW4oYSxjKSkKcmV0dXJuIXMuZ2wwKHMpfX0sCkE0OmZ1bmN0
+aW9uKGEpe2lmKGEuaW5kZXhPZigiJCIsMCk+PTApcmV0dXJuIGEucmVwbGFjZSgvXCQvZywiJCQkJCIp
+CnJldHVybiBhfSwKZUE6ZnVuY3Rpb24oYSl7aWYoL1tbXF17fSgpKis/LlxcXiR8XS8udGVzdChhKSly
+ZXR1cm4gYS5yZXBsYWNlKC9bW1xde30oKSorPy5cXF4kfF0vZywiXFwkJiIpCnJldHVybiBhfSwKeXM6
+ZnVuY3Rpb24oYSxiLGMpe3ZhciBzPUgubk0oYSxiLGMpCnJldHVybiBzfSwKbk06ZnVuY3Rpb24oYSxi
+LGMpe3ZhciBzLHIscSxwCmlmKGI9PT0iIil7aWYoYT09PSIiKXJldHVybiBjCnM9YS5sZW5ndGgKcj0i
+IitjCmZvcihxPTA7cTxzOysrcSlyPXIrYVtxXStjCnJldHVybiByLmNoYXJDb2RlQXQoMCk9PTA/cjpy
+fXA9YS5pbmRleE9mKGIsMCkKaWYocDwwKXJldHVybiBhCmlmKGEubGVuZ3RoPDUwMHx8Yy5pbmRleE9m
+KCIkIiwwKT49MClyZXR1cm4gYS5zcGxpdChiKS5qb2luKGMpCnJldHVybiBhLnJlcGxhY2UobmV3IFJl
+Z0V4cChILmVBKGIpLCJnIiksSC5BNChjKSl9LApQRDpmdW5jdGlvbiBQRChhLGIpe3RoaXMuYT1hCnRo
+aXMuJHRpPWJ9LApXVTpmdW5jdGlvbiBXVSgpe30sCkxQOmZ1bmN0aW9uIExQKGEsYixjLGQpe3ZhciBf
+PXRoaXMKXy5hPWEKXy5iPWIKXy5jPWMKXy4kdGk9ZH0sClhSOmZ1bmN0aW9uIFhSKGEsYil7dGhpcy5h
+PWEKdGhpcy4kdGk9Yn0sCkxJOmZ1bmN0aW9uIExJKGEsYixjLGQsZSl7dmFyIF89dGhpcwpfLmE9YQpf
+LmM9YgpfLmQ9YwpfLmU9ZApfLmY9ZX0sCkNqOmZ1bmN0aW9uIENqKGEsYixjKXt0aGlzLmE9YQp0aGlz
+LmI9Ygp0aGlzLmM9Y30sCmY5OmZ1bmN0aW9uIGY5KGEsYixjLGQsZSxmKXt2YXIgXz10aGlzCl8uYT1h
+Cl8uYj1iCl8uYz1jCl8uZD1kCl8uZT1lCl8uZj1mfSwKVzA6ZnVuY3Rpb24gVzAoYSxiKXt0aGlzLmE9
+YQp0aGlzLmI9Yn0sCmF6OmZ1bmN0aW9uIGF6KGEsYixjKXt0aGlzLmE9YQp0aGlzLmI9Ygp0aGlzLmM9
+Y30sCnZWOmZ1bmN0aW9uIHZWKGEpe3RoaXMuYT1hfSwKdGU6ZnVuY3Rpb24gdGUoYSl7dGhpcy5hPWF9
+LApicTpmdW5jdGlvbiBicShhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKWE86ZnVuY3Rpb24gWE8oYSl7
+dGhpcy5hPWEKdGhpcy5iPW51bGx9LApUcDpmdW5jdGlvbiBUcCgpe30sCmxjOmZ1bmN0aW9uIGxjKCl7
+fSwKeng6ZnVuY3Rpb24gengoKXt9LApyVDpmdW5jdGlvbiByVChhLGIpe3RoaXMuYT1hCnRoaXMuYj1i
+fSwKRXE6ZnVuY3Rpb24gRXEoYSl7dGhpcy5hPWF9LAprWTpmdW5jdGlvbiBrWShhKXt0aGlzLmE9YX0s
+CmtyOmZ1bmN0aW9uIGtyKCl7fSwKTjU6ZnVuY3Rpb24gTjUoYSl7dmFyIF89dGhpcwpfLmE9MApfLmY9
+Xy5lPV8uZD1fLmM9Xy5iPW51bGwKXy5yPTAKXy4kdGk9YX0sCnZoOmZ1bmN0aW9uIHZoKGEsYil7dmFy
+IF89dGhpcwpfLmE9YQpfLmI9YgpfLmQ9Xy5jPW51bGx9LAppNTpmdW5jdGlvbiBpNShhLGIpe3RoaXMu
+YT1hCnRoaXMuJHRpPWJ9LApONjpmdW5jdGlvbiBONihhLGIsYyl7dmFyIF89dGhpcwpfLmE9YQpfLmI9
+YgpfLmQ9Xy5jPW51bGwKXy4kdGk9Y30sCmRDOmZ1bmN0aW9uIGRDKGEpe3RoaXMuYT1hfSwKd046ZnVu
+Y3Rpb24gd04oYSl7dGhpcy5hPWF9LApWWDpmdW5jdGlvbiBWWChhKXt0aGlzLmE9YX0sClZSOmZ1bmN0
+aW9uIFZSKGEsYil7dmFyIF89dGhpcwpfLmE9YQpfLmI9YgpfLmQ9Xy5jPW51bGx9LApFSzpmdW5jdGlv
+biBFSyhhKXt0aGlzLmI9YX0sCktXOmZ1bmN0aW9uIEtXKGEsYixjKXt0aGlzLmE9YQp0aGlzLmI9Ygp0
+aGlzLmM9Y30sClBiOmZ1bmN0aW9uIFBiKGEsYixjKXt2YXIgXz10aGlzCl8uYT1hCl8uYj1iCl8uYz1j
+Cl8uZD1udWxsfSwKdFE6ZnVuY3Rpb24gdFEoYSxiKXt0aGlzLmE9YQp0aGlzLmM9Yn0sCnVuOmZ1bmN0
+aW9uIHVuKGEsYixjKXt0aGlzLmE9YQp0aGlzLmI9Ygp0aGlzLmM9Y30sClNkOmZ1bmN0aW9uIFNkKGEs
+YixjKXt2YXIgXz10aGlzCl8uYT1hCl8uYj1iCl8uYz1jCl8uZD1udWxsfSwKWEY6ZnVuY3Rpb24oYSl7
+cmV0dXJuIGF9LApvZDpmdW5jdGlvbihhLGIsYyl7aWYoYT4+PjAhPT1hfHxhPj1jKXRocm93IEguYihI
+LnUoYixhKSl9LApyTTpmdW5jdGlvbihhLGIsYyl7dmFyIHMKaWYoIShhPj4+MCE9PWEpKXM9Yj4+PjAh
+PT1ifHxhPmJ8fGI+YwplbHNlIHM9ITAKaWYocyl0aHJvdyBILmIoSC5hdShhLGIsYykpCnJldHVybiBi
+fSwKRVQ6ZnVuY3Rpb24gRVQoKXt9LApMWjpmdW5jdGlvbiBMWigpe30sCkRnOmZ1bmN0aW9uIERnKCl7
+fSwKUGc6ZnVuY3Rpb24gUGcoKXt9LAp4ajpmdW5jdGlvbiB4aigpe30sCmRFOmZ1bmN0aW9uIGRFKCl7
+fSwKWkE6ZnVuY3Rpb24gWkEoKXt9LApkVDpmdW5jdGlvbiBkVCgpe30sClBxOmZ1bmN0aW9uIFBxKCl7
+fSwKZUU6ZnVuY3Rpb24gZUUoKXt9LApWNjpmdW5jdGlvbiBWNigpe30sClJHOmZ1bmN0aW9uIFJHKCl7
+fSwKVlA6ZnVuY3Rpb24gVlAoKXt9LApXQjpmdW5jdGlvbiBXQigpe30sClpHOmZ1bmN0aW9uIFpHKCl7
+fSwKY3o6ZnVuY3Rpb24oYSxiKXt2YXIgcz1iLmMKcmV0dXJuIHM9PW51bGw/Yi5jPUguQihhLGIueiwh
+MCk6c30sCnhaOmZ1bmN0aW9uKGEsYil7dmFyIHM9Yi5jCnJldHVybiBzPT1udWxsP2IuYz1ILkooYSwi
+YjgiLFtiLnpdKTpzfSwKUTE6ZnVuY3Rpb24oYSl7dmFyIHM9YS55CmlmKHM9PT02fHxzPT09N3x8cz09
+PTgpcmV0dXJuIEguUTEoYS56KQpyZXR1cm4gcz09PTExfHxzPT09MTJ9LAptRDpmdW5jdGlvbihhKXty
+ZXR1cm4gYS5jeX0sCk4wOmZ1bmN0aW9uKGEpe3JldHVybiBILkUodi50eXBlVW5pdmVyc2UsYSwhMSl9
+LApQTDpmdW5jdGlvbihhLGIsYTAsYTEpe3ZhciBzLHIscSxwLG8sbixtLGwsayxqLGksaCxnLGYsZSxk
+LGM9Yi55CnN3aXRjaChjKXtjYXNlIDU6Y2FzZSAxOmNhc2UgMjpjYXNlIDM6Y2FzZSA0OnJldHVybiBi
+CmNhc2UgNjpzPWIuegpyPUguUEwoYSxzLGEwLGExKQppZihyPT09cylyZXR1cm4gYgpyZXR1cm4gSC5D
+KGEsciwhMCkKY2FzZSA3OnM9Yi56CnI9SC5QTChhLHMsYTAsYTEpCmlmKHI9PT1zKXJldHVybiBiCnJl
+dHVybiBILkIoYSxyLCEwKQpjYXNlIDg6cz1iLnoKcj1ILlBMKGEscyxhMCxhMSkKaWYocj09PXMpcmV0
+dXJuIGIKcmV0dXJuIEguZihhLHIsITApCmNhc2UgOTpxPWIuUQpwPUguYlooYSxxLGEwLGExKQppZihw
+PT09cSlyZXR1cm4gYgpyZXR1cm4gSC5KKGEsYi56LHApCmNhc2UgMTA6bz1iLnoKbj1ILlBMKGEsbyxh
+MCxhMSkKbT1iLlEKbD1ILmJaKGEsbSxhMCxhMSkKaWYobj09PW8mJmw9PT1tKXJldHVybiBiCnJldHVy
+biBILmEoYSxuLGwpCmNhc2UgMTE6az1iLnoKaj1ILlBMKGEsayxhMCxhMSkKaT1iLlEKaD1ILnFUKGEs
+aSxhMCxhMSkKaWYoaj09PWsmJmg9PT1pKXJldHVybiBiCnJldHVybiBILmQoYSxqLGgpCmNhc2UgMTI6
+Zz1iLlEKYTErPWcubGVuZ3RoCmY9SC5iWihhLGcsYTAsYTEpCm89Yi56Cm49SC5QTChhLG8sYTAsYTEp
+CmlmKGY9PT1nJiZuPT09bylyZXR1cm4gYgpyZXR1cm4gSC5EKGEsbixmLCEwKQpjYXNlIDEzOmU9Yi56
+CmlmKGU8YTEpcmV0dXJuIGIKZD1hMFtlLWExXQppZihkPT1udWxsKXJldHVybiBiCnJldHVybiBkCmRl
+ZmF1bHQ6dGhyb3cgSC5iKFAuaFYoIkF0dGVtcHRlZCB0byBzdWJzdGl0dXRlIHVuZXhwZWN0ZWQgUlRJ
+IGtpbmQgIitjKSl9fSwKYlo6ZnVuY3Rpb24oYSxiLGMsZCl7dmFyIHMscixxLHAsbz1iLmxlbmd0aCxu
+PVtdCmZvcihzPSExLHI9MDtyPG87KytyKXtxPWJbcl0KcD1ILlBMKGEscSxjLGQpCmlmKHAhPT1xKXM9
+ITAKbi5wdXNoKHApfXJldHVybiBzP246Yn0sCnZPOmZ1bmN0aW9uKGEsYixjLGQpe3ZhciBzLHIscSxw
+LG8sbixtPWIubGVuZ3RoLGw9W10KZm9yKHM9ITEscj0wO3I8bTtyKz0zKXtxPWJbcl0KcD1iW3IrMV0K
+bz1iW3IrMl0Kbj1ILlBMKGEsbyxjLGQpCmlmKG4hPT1vKXM9ITAKbC5wdXNoKHEpCmwucHVzaChwKQps
+LnB1c2gobil9cmV0dXJuIHM/bDpifSwKcVQ6ZnVuY3Rpb24oYSxiLGMsZCl7dmFyIHMscj1iLmEscT1I
+LmJaKGEscixjLGQpLHA9Yi5iLG89SC5iWihhLHAsYyxkKSxuPWIuYyxtPUgudk8oYSxuLGMsZCkKaWYo
+cT09PXImJm89PT1wJiZtPT09bilyZXR1cm4gYgpzPW5ldyBILkcoKQpzLmE9cQpzLmI9bwpzLmM9bQpy
+ZXR1cm4gc30sClFJOmZ1bmN0aW9uKGEsYil7YVt2LmFycmF5UnRpXT1iCnJldHVybiBhfSwKSlM6ZnVu
+Y3Rpb24oYSl7dmFyIHM9YS4kUwppZihzIT1udWxsKXtpZih0eXBlb2Ygcz09Im51bWJlciIpcmV0dXJu
+IEguQnAocykKcmV0dXJuIGEuJFMoKX1yZXR1cm4gbnVsbH0sClVlOmZ1bmN0aW9uKGEsYil7dmFyIHMK
+aWYoSC5RMShiKSlpZihhIGluc3RhbmNlb2YgSC5UcCl7cz1ILkpTKGEpCmlmKHMhPW51bGwpcmV0dXJu
+IHN9cmV0dXJuIEgueksoYSl9LAp6SzpmdW5jdGlvbihhKXt2YXIgcwppZihhIGluc3RhbmNlb2YgUC5N
+aCl7cz1hLiR0aQpyZXR1cm4gcyE9bnVsbD9zOkguVlUoYSl9aWYoQXJyYXkuaXNBcnJheShhKSlyZXR1
+cm4gSC50NihhKQpyZXR1cm4gSC5WVShKLmlhKGEpKX0sCnQ2OmZ1bmN0aW9uKGEpe3ZhciBzPWFbdi5h
+cnJheVJ0aV0scj10LmIKaWYocz09bnVsbClyZXR1cm4gcgppZihzLmNvbnN0cnVjdG9yIT09ci5jb25z
+dHJ1Y3RvcilyZXR1cm4gcgpyZXR1cm4gc30sCkxoOmZ1bmN0aW9uKGEpe3ZhciBzPWEuJHRpCnJldHVy
+biBzIT1udWxsP3M6SC5WVShhKX0sClZVOmZ1bmN0aW9uKGEpe3ZhciBzPWEuY29uc3RydWN0b3Iscj1z
+LiRjY2FjaGUKaWYociE9bnVsbClyZXR1cm4gcgpyZXR1cm4gSC5yOShhLHMpfSwKcjk6ZnVuY3Rpb24o
+YSxiKXt2YXIgcz1hIGluc3RhbmNlb2YgSC5UcD9hLl9fcHJvdG9fXy5fX3Byb3RvX18uY29uc3RydWN0
+b3I6YixyPUguYWkodi50eXBlVW5pdmVyc2Uscy5uYW1lKQpiLiRjY2FjaGU9cgpyZXR1cm4gcn0sCkJw
+OmZ1bmN0aW9uKGEpe3ZhciBzLHIscQpILklaKGEpCnM9di50eXBlcwpyPXNbYV0KaWYodHlwZW9mIHI9
+PSJzdHJpbmciKXtxPUguRSh2LnR5cGVVbml2ZXJzZSxyLCExKQpzW2FdPXEKcmV0dXJuIHF9cmV0dXJu
+IHJ9LApKSjpmdW5jdGlvbihhKXt2YXIgcyxyLHEscCxvPXRoaXMKaWYobz09PXQuSylyZXR1cm4gSC5S
+RShvLGEsSC5rZSkKaWYoIUguQTgobykpaWYoIShvPT09dC5fKSlzPSExCmVsc2Ugcz0hMAplbHNlIHM9
+ITAKaWYocylyZXR1cm4gSC5SRShvLGEsSC5JdykKcz1vLnkKcj1zPT09Nj9vLno6bwppZihyPT09dC5T
+KXE9SC5vawplbHNlIGlmKHI9PT10LmdSfHxyPT09dC5kaSlxPUguS0gKZWxzZSBpZihyPT09dC5OKXE9
+SC5NTQplbHNlIHE9cj09PXQueT9ILnJROm51bGwKaWYocSE9bnVsbClyZXR1cm4gSC5SRShvLGEscSkK
+aWYoci55PT09OSl7cD1yLnoKaWYoci5RLmV2ZXJ5KEguY2MpKXtvLnI9IiRpIitwCmlmKHA9PT0iek0i
+KXJldHVybiBILlJFKG8sYSxILnlNKQpyZXR1cm4gSC5SRShvLGEsSC50NCl9fWVsc2UgaWYocz09PTcp
+cmV0dXJuIEguUkUobyxhLEguQVEpCnJldHVybiBILlJFKG8sYSxILllPKX0sClJFOmZ1bmN0aW9uKGEs
+YixjKXthLmI9YwpyZXR1cm4gYS5iKGIpfSwKQXU6ZnVuY3Rpb24oYSl7dmFyIHMscj10aGlzLHE9SC5P
+egppZighSC5BOChyKSlpZighKHI9PT10Ll8pKXM9ITEKZWxzZSBzPSEwCmVsc2Ugcz0hMAppZihzKXE9
+SC5obgplbHNlIGlmKHI9PT10LkspcT1ILlRpCmVsc2V7cz1ILmxSKHIpCmlmKHMpcT1ILmw0fXIuYT1x
+CnJldHVybiByLmEoYSl9LApRajpmdW5jdGlvbihhKXt2YXIgcyxyPWEueQppZighSC5BOChhKSlpZigh
+KGE9PT10Ll8pKWlmKCEoYT09PXQuYXcpKWlmKHIhPT03KXM9cj09PTgmJkguUWooYS56KXx8YT09PXQu
+UHx8YT09PXQuVAplbHNlIHM9ITAKZWxzZSBzPSEwCmVsc2Ugcz0hMAplbHNlIHM9ITAKcmV0dXJuIHN9
+LApZTzpmdW5jdGlvbihhKXt2YXIgcz10aGlzCmlmKGE9PW51bGwpcmV0dXJuIEguUWoocykKcmV0dXJu
+IEguV2Uodi50eXBlVW5pdmVyc2UsSC5VZShhLHMpLG51bGwscyxudWxsKX0sCkFROmZ1bmN0aW9uKGEp
+e2lmKGE9PW51bGwpcmV0dXJuITAKcmV0dXJuIHRoaXMuei5iKGEpfSwKdDQ6ZnVuY3Rpb24oYSl7dmFy
+IHMscj10aGlzCmlmKGE9PW51bGwpcmV0dXJuIEguUWoocikKcz1yLnIKaWYoYSBpbnN0YW5jZW9mIFAu
+TWgpcmV0dXJuISFhW3NdCnJldHVybiEhSi5pYShhKVtzXX0sCnlNOmZ1bmN0aW9uKGEpe3ZhciBzLHI9
+dGhpcwppZihhPT1udWxsKXJldHVybiBILlFqKHIpCmlmKHR5cGVvZiBhIT0ib2JqZWN0IilyZXR1cm4h
+MQppZihBcnJheS5pc0FycmF5KGEpKXJldHVybiEwCnM9ci5yCmlmKGEgaW5zdGFuY2VvZiBQLk1oKXJl
+dHVybiEhYVtzXQpyZXR1cm4hIUouaWEoYSlbc119LApPejpmdW5jdGlvbihhKXt2YXIgcyxyPXRoaXMK
+aWYoYT09bnVsbCl7cz1ILmxSKHIpCmlmKHMpcmV0dXJuIGF9ZWxzZSBpZihyLmIoYSkpcmV0dXJuIGEK
+SC5tNChhLHIpfSwKbDQ6ZnVuY3Rpb24oYSl7dmFyIHM9dGhpcwppZihhPT1udWxsKXJldHVybiBhCmVs
+c2UgaWYocy5iKGEpKXJldHVybiBhCkgubTQoYSxzKX0sCm00OmZ1bmN0aW9uKGEsYil7dGhyb3cgSC5i
+KEguWmMoSC5XSyhhLEguVWUoYSxiKSxILmRtKGIsbnVsbCkpKSl9LApEaDpmdW5jdGlvbihhLGIsYyxk
+KXt2YXIgcz1udWxsCmlmKEguV2Uodi50eXBlVW5pdmVyc2UsYSxzLGIscykpcmV0dXJuIGEKdGhyb3cg
+SC5iKEguWmMoIlRoZSB0eXBlIGFyZ3VtZW50ICciK0guZG0oYSxzKSsiJyBpcyBub3QgYSBzdWJ0eXBl
+IG9mIHRoZSB0eXBlIHZhcmlhYmxlIGJvdW5kICciK0guZG0oYixzKSsiJyBvZiB0eXBlIHZhcmlhYmxl
+ICciK2MrIicgaW4gJyIrZCsiJy4iKSl9LApXSzpmdW5jdGlvbihhLGIsYyl7dmFyIHM9UC5obChhKSxy
+PUguZG0oYj09bnVsbD9ILnpLKGEpOmIsbnVsbCkKcmV0dXJuIHMrIjogdHlwZSAnIityKyInIGlzIG5v
+dCBhIHN1YnR5cGUgb2YgdHlwZSAnIitjKyInIn0sClpjOmZ1bmN0aW9uKGEpe3JldHVybiBuZXcgSC5p
+TSgiVHlwZUVycm9yOiAiK2EpfSwKcTpmdW5jdGlvbihhLGIpe3JldHVybiBuZXcgSC5pTSgiVHlwZUVy
+cm9yOiAiK0guV0soYSxudWxsLGIpKX0sCmtlOmZ1bmN0aW9uKGEpe3JldHVybiBhIT1udWxsfSwKVGk6
+ZnVuY3Rpb24oYSl7aWYoYSE9bnVsbClyZXR1cm4gYQp0aHJvdyBILmIoSC5xKGEsIk9iamVjdCIpKX0s
+Ckl3OmZ1bmN0aW9uKGEpe3JldHVybiEwfSwKaG46ZnVuY3Rpb24oYSl7cmV0dXJuIGF9LApyUTpmdW5j
+dGlvbihhKXtyZXR1cm4hMD09PWF8fCExPT09YX0sCnA4OmZ1bmN0aW9uKGEpe2lmKCEwPT09YSlyZXR1
+cm4hMAppZighMT09PWEpcmV0dXJuITEKdGhyb3cgSC5iKEgucShhLCJib29sIikpfSwKbUw6ZnVuY3Rp
+b24oYSl7aWYoITA9PT1hKXJldHVybiEwCmlmKCExPT09YSlyZXR1cm4hMQppZihhPT1udWxsKXJldHVy
+biBhCnRocm93IEguYihILnEoYSwiYm9vbCIpKX0sCk00OmZ1bmN0aW9uKGEpe2lmKCEwPT09YSlyZXR1
+cm4hMAppZighMT09PWEpcmV0dXJuITEKaWYoYT09bnVsbClyZXR1cm4gYQp0aHJvdyBILmIoSC5xKGEs
+ImJvb2w/IikpfSwKclY6ZnVuY3Rpb24oYSl7aWYodHlwZW9mIGE9PSJudW1iZXIiKXJldHVybiBhCnRo
+cm93IEguYihILnEoYSwiZG91YmxlIikpfSwKdEY6ZnVuY3Rpb24oYSl7aWYodHlwZW9mIGE9PSJudW1i
+ZXIiKXJldHVybiBhCmlmKGE9PW51bGwpcmV0dXJuIGEKdGhyb3cgSC5iKEgucShhLCJkb3VibGUiKSl9
+LApRazpmdW5jdGlvbihhKXtpZih0eXBlb2YgYT09Im51bWJlciIpcmV0dXJuIGEKaWYoYT09bnVsbCly
+ZXR1cm4gYQp0aHJvdyBILmIoSC5xKGEsImRvdWJsZT8iKSl9LApvazpmdW5jdGlvbihhKXtyZXR1cm4g
+dHlwZW9mIGE9PSJudW1iZXIiJiZNYXRoLmZsb29yKGEpPT09YX0sCklaOmZ1bmN0aW9uKGEpe2lmKHR5
+cGVvZiBhPT0ibnVtYmVyIiYmTWF0aC5mbG9vcihhKT09PWEpcmV0dXJuIGEKdGhyb3cgSC5iKEgucShh
+LCJpbnQiKSl9LAp1UDpmdW5jdGlvbihhKXtpZih0eXBlb2YgYT09Im51bWJlciImJk1hdGguZmxvb3Io
+YSk9PT1hKXJldHVybiBhCmlmKGE9PW51bGwpcmV0dXJuIGEKdGhyb3cgSC5iKEgucShhLCJpbnQiKSl9
+LApVYzpmdW5jdGlvbihhKXtpZih0eXBlb2YgYT09Im51bWJlciImJk1hdGguZmxvb3IoYSk9PT1hKXJl
+dHVybiBhCmlmKGE9PW51bGwpcmV0dXJuIGEKdGhyb3cgSC5iKEgucShhLCJpbnQ/IikpfSwKS0g6ZnVu
+Y3Rpb24oYSl7cmV0dXJuIHR5cGVvZiBhPT0ibnVtYmVyIn0sCno1OmZ1bmN0aW9uKGEpe2lmKHR5cGVv
+ZiBhPT0ibnVtYmVyIilyZXR1cm4gYQp0aHJvdyBILmIoSC5xKGEsIm51bSIpKX0sClcxOmZ1bmN0aW9u
+KGEpe2lmKHR5cGVvZiBhPT0ibnVtYmVyIilyZXR1cm4gYQppZihhPT1udWxsKXJldHVybiBhCnRocm93
+IEguYihILnEoYSwibnVtIikpfSwKY1U6ZnVuY3Rpb24oYSl7aWYodHlwZW9mIGE9PSJudW1iZXIiKXJl
+dHVybiBhCmlmKGE9PW51bGwpcmV0dXJuIGEKdGhyb3cgSC5iKEgucShhLCJudW0/IikpfSwKTU06ZnVu
+Y3Rpb24oYSl7cmV0dXJuIHR5cGVvZiBhPT0ic3RyaW5nIn0sCm46ZnVuY3Rpb24oYSl7aWYodHlwZW9m
+IGE9PSJzdHJpbmciKXJldHVybiBhCnRocm93IEguYihILnEoYSwiU3RyaW5nIikpfSwKaE46ZnVuY3Rp
b24oYSl7aWYodHlwZW9mIGE9PSJzdHJpbmciKXJldHVybiBhCmlmKGE9PW51bGwpcmV0dXJuIGEKdGhy
-b3cgSC5iKEgucShhLCJTdHJpbmc/IikpfSwKaW86ZnVuY3Rpb24oYSxiKXt2YXIgcyxyLHEKZm9yKHM9
-IiIscj0iIixxPTA7cTxhLmxlbmd0aDsrK3Escj0iLCAiKXMrPUMueEIuaChyLEguZG0oYVtxXSxiKSkK
-cmV0dXJuIHN9LApiSTpmdW5jdGlvbihhNSxhNixhNyl7dmFyIHMscixxLHAsbyxuLG0sbCxrLGosaSxo
-LGcsZixlLGQsYyxiLGEsYTAsYTEsYTIsYTMsYTQ9IiwgIgppZihhNyE9bnVsbCl7cz1hNy5sZW5ndGgK
-aWYoYTY9PW51bGwpe2E2PUguUUkoW10sdC5zKQpyPW51bGx9ZWxzZSByPWE2Lmxlbmd0aApxPWE2Lmxl
-bmd0aApmb3IocD1zO3A+MDstLXApQy5ObS5pKGE2LCJUIisocStwKSkKZm9yKG89dC5XLG49dC5fLG09
-dC5LLGw9IjwiLGs9IiIscD0wO3A8czsrK3Asaz1hNCl7bCs9awpqPWE2Lmxlbmd0aAppPWotMS1wCmlm
-KGk8MClyZXR1cm4gSC5PSChhNixpKQpsPUMueEIuaChsLGE2W2ldKQpoPWE3W3BdCmc9aC55CmlmKCEo
-Zz09PTJ8fGc9PT0zfHxnPT09NHx8Zz09PTV8fGg9PT1vKSlpZighKGg9PT1uKSlqPWg9PT1tCmVsc2Ug
-aj0hMAplbHNlIGo9ITAKaWYoIWopbCs9Qy54Qi5oKCIgZXh0ZW5kcyAiLEguZG0oaCxhNikpfWwrPSI+
-In1lbHNle2w9IiIKcj1udWxsfW89YTUuegpmPWE1LlEKZT1mLmEKZD1lLmxlbmd0aApjPWYuYgpiPWMu
-bGVuZ3RoCmE9Zi5jCmEwPWEubGVuZ3RoCmExPUguZG0obyxhNikKZm9yKGEyPSIiLGEzPSIiLHA9MDtw
-PGQ7KytwLGEzPWE0KWEyKz1DLnhCLmgoYTMsSC5kbShlW3BdLGE2KSkKaWYoYj4wKXthMis9YTMrIlsi
-CmZvcihhMz0iIixwPTA7cDxiOysrcCxhMz1hNClhMis9Qy54Qi5oKGEzLEguZG0oY1twXSxhNikpCmEy
-Kz0iXSJ9aWYoYTA+MCl7YTIrPWEzKyJ7Igpmb3IoYTM9IiIscD0wO3A8YTA7cCs9MyxhMz1hNCl7YTIr
-PWEzCmlmKGFbcCsxXSlhMis9InJlcXVpcmVkICIKYTIrPUoucGIoSC5kbShhW3ArMl0sYTYpLCIgIikr
-YVtwXX1hMis9In0ifWlmKHIhPW51bGwpe2E2LnRvU3RyaW5nCmE2Lmxlbmd0aD1yfXJldHVybiBsKyIo
-IithMisiKSA9PiAiK0guRWooYTEpfSwKZG06ZnVuY3Rpb24oYSxiKXt2YXIgcyxyLHEscCxvLG4sbSxs
-PWEueQppZihsPT09NSlyZXR1cm4iZXJhc2VkIgppZihsPT09MilyZXR1cm4iZHluYW1pYyIKaWYobD09
-PTMpcmV0dXJuInZvaWQiCmlmKGw9PT0xKXJldHVybiJOZXZlciIKaWYobD09PTQpcmV0dXJuImFueSIK
-aWYobD09PTYpe3M9SC5kbShhLnosYikKcmV0dXJuIHN9aWYobD09PTcpe3I9YS56CnM9SC5kbShyLGIp
-CnE9ci55CnJldHVybiBKLnBiKHE9PT0xMXx8cT09PTEyP0MueEIuaCgiKCIscykrIikiOnMsIj8iKX1p
-ZihsPT09OClyZXR1cm4iRnV0dXJlT3I8IitILkVqKEguZG0oYS56LGIpKSsiPiIKaWYobD09PTkpe3A9
-SC5vMyhhLnopCm89YS5RCnJldHVybiBvLmxlbmd0aCE9PTA/cCsoIjwiK0guaW8obyxiKSsiPiIpOnB9
-aWYobD09PTExKXJldHVybiBILmJJKGEsYixudWxsKQppZihsPT09MTIpcmV0dXJuIEguYkkoYS56LGIs
-YS5RKQppZihsPT09MTMpe2IudG9TdHJpbmcKbj1hLnoKbT1iLmxlbmd0aApuPW0tMS1uCmlmKG48MHx8
-bj49bSlyZXR1cm4gSC5PSChiLG4pCnJldHVybiBiW25dfXJldHVybiI/In0sCm8zOmZ1bmN0aW9uKGEp
-e3ZhciBzLHI9di5tYW5nbGVkR2xvYmFsTmFtZXNbYV0KaWYociE9bnVsbClyZXR1cm4gcgpzPSJtaW5p
-ZmllZDoiK2EKcmV0dXJuIHN9LApRbzpmdW5jdGlvbihhLGIpe3ZhciBzPWEudFJbYl0KZm9yKDt0eXBl
-b2Ygcz09InN0cmluZyI7KXM9YS50UltzXQpyZXR1cm4gc30sCmFpOmZ1bmN0aW9uKGEsYil7dmFyIHMs
-cixxLHAsbyxuPWEuZVQsbT1uW2JdCmlmKG09PW51bGwpcmV0dXJuIEguRShhLGIsITEpCmVsc2UgaWYo
-dHlwZW9mIG09PSJudW1iZXIiKXtzPW0Kcj1ILm0oYSw1LCIjIikKcT1bXQpmb3IocD0wO3A8czsrK3Ap
-cS5wdXNoKHIpCm89SC5KKGEsYixxKQpuW2JdPW8KcmV0dXJuIG99ZWxzZSByZXR1cm4gbX0sCnhiOmZ1
-bmN0aW9uKGEsYil7cmV0dXJuIEguSXgoYS50UixiKX0sCkZGOmZ1bmN0aW9uKGEsYil7cmV0dXJuIEgu
-SXgoYS5lVCxiKX0sCkU6ZnVuY3Rpb24oYSxiLGMpe3ZhciBzLHI9YS5lQyxxPXIuZ2V0KGIpCmlmKHEh
-PW51bGwpcmV0dXJuIHEKcz1ILmkoSC5vKGEsbnVsbCxiLGMpKQpyLnNldChiLHMpCnJldHVybiBzfSwK
-Y0U6ZnVuY3Rpb24oYSxiLGMpe3ZhciBzLHIscT1iLmNoCmlmKHE9PW51bGwpcT1iLmNoPW5ldyBNYXAo
-KQpzPXEuZ2V0KGMpCmlmKHMhPW51bGwpcmV0dXJuIHMKcj1ILmkoSC5vKGEsYixjLCEwKSkKcS5zZXQo
-YyxyKQpyZXR1cm4gcn0sCnY1OmZ1bmN0aW9uKGEsYixjKXt2YXIgcyxyLHEscD1iLmN4CmlmKHA9PW51
-bGwpcD1iLmN4PW5ldyBNYXAoKQpzPWMuY3kKcj1wLmdldChzKQppZihyIT1udWxsKXJldHVybiByCnE9
-SC5hKGEsYixjLnk9PT0xMD9jLlE6W2NdKQpwLnNldChzLHEpCnJldHVybiBxfSwKQkQ6ZnVuY3Rpb24o
-YSxiKXtiLmE9SC5BdQpiLmI9SC5KSgpyZXR1cm4gYn0sCm06ZnVuY3Rpb24oYSxiLGMpe3ZhciBzLHIs
-cT1hLmVDLmdldChjKQppZihxIT1udWxsKXJldHVybiBxCnM9bmV3IEguSmMobnVsbCxudWxsKQpzLnk9
-YgpzLmN5PWMKcj1ILkJEKGEscykKYS5lQy5zZXQoYyxyKQpyZXR1cm4gcn0sCkM6ZnVuY3Rpb24oYSxi
-LGMpe3ZhciBzLHI9Yi5jeSsiKiIscT1hLmVDLmdldChyKQppZihxIT1udWxsKXJldHVybiBxCnM9SC5a
-NyhhLGIscixjKQphLmVDLnNldChyLHMpCnJldHVybiBzfSwKWjc6ZnVuY3Rpb24oYSxiLGMsZCl7dmFy
-IHMscixxCmlmKGQpe3M9Yi55CmlmKCFILkE4KGIpKXI9Yj09PXQuUHx8Yj09PXQuVHx8cz09PTd8fHM9
-PT02CmVsc2Ugcj0hMAppZihyKXJldHVybiBifXE9bmV3IEguSmMobnVsbCxudWxsKQpxLnk9NgpxLno9
-YgpxLmN5PWMKcmV0dXJuIEguQkQoYSxxKX0sCkI6ZnVuY3Rpb24oYSxiLGMpe3ZhciBzLHI9Yi5jeSsi
-PyIscT1hLmVDLmdldChyKQppZihxIT1udWxsKXJldHVybiBxCnM9SC5sbChhLGIscixjKQphLmVDLnNl
-dChyLHMpCnJldHVybiBzfSwKbGw6ZnVuY3Rpb24oYSxiLGMsZCl7dmFyIHMscixxLHAKaWYoZCl7cz1i
-LnkKaWYoIUguQTgoYikpaWYoIShiPT09dC5QfHxiPT09dC5UKSlpZihzIT09NylyPXM9PT04JiZILmxS
-KGIueikKZWxzZSByPSEwCmVsc2Ugcj0hMAplbHNlIHI9ITAKaWYocilyZXR1cm4gYgplbHNlIGlmKHM9
-PT0xfHxiPT09dC5jRilyZXR1cm4gdC5QCmVsc2UgaWYocz09PTYpe3E9Yi56CmlmKHEueT09PTgmJkgu
-bFIocS56KSlyZXR1cm4gcQplbHNlIHJldHVybiBILmN6KGEsYil9fXA9bmV3IEguSmMobnVsbCxudWxs
-KQpwLnk9NwpwLno9YgpwLmN5PWMKcmV0dXJuIEguQkQoYSxwKX0sCmY6ZnVuY3Rpb24oYSxiLGMpe3Zh
-ciBzLHI9Yi5jeSsiLyIscT1hLmVDLmdldChyKQppZihxIT1udWxsKXJldHVybiBxCnM9SC5lVihhLGIs
-cixjKQphLmVDLnNldChyLHMpCnJldHVybiBzfSwKZVY6ZnVuY3Rpb24oYSxiLGMsZCl7dmFyIHMscixx
-CmlmKGQpe3M9Yi55CmlmKCFILkE4KGIpKWlmKCEoYj09PXQuXykpcj1iPT09dC5LCmVsc2Ugcj0hMApl
+b3cgSC5iKEgucShhLCJTdHJpbmciKSl9LAprOmZ1bmN0aW9uKGEpe2lmKHR5cGVvZiBhPT0ic3RyaW5n
+IilyZXR1cm4gYQppZihhPT1udWxsKXJldHVybiBhCnRocm93IEguYihILnEoYSwiU3RyaW5nPyIpKX0s
+CmlvOmZ1bmN0aW9uKGEsYil7dmFyIHMscixxCmZvcihzPSIiLHI9IiIscT0wO3E8YS5sZW5ndGg7Kytx
+LHI9IiwgIilzKz1yK0guZG0oYVtxXSxiKQpyZXR1cm4gc30sCmJJOmZ1bmN0aW9uKGE0LGE1LGE2KXt2
+YXIgcyxyLHEscCxvLG4sbSxsLGssaixpLGgsZyxmLGUsZCxjLGIsYSxhMCxhMSxhMixhMz0iLCAiCmlm
+KGE2IT1udWxsKXtzPWE2Lmxlbmd0aAppZihhNT09bnVsbCl7YTU9SC5RSShbXSx0LnMpCnI9bnVsbH1l
+bHNlIHI9YTUubGVuZ3RoCnE9YTUubGVuZ3RoCmZvcihwPXM7cD4wOy0tcClDLk5tLmkoYTUsIlQiKyhx
+K3ApKQpmb3Iobz10Llgsbj10Ll8sbT0iPCIsbD0iIixwPTA7cDxzOysrcCxsPWEzKXttKz1sCms9YTUu
+bGVuZ3RoCmo9ay0xLXAKaWYoajwwKXJldHVybiBILk9IKGE1LGopCm09Qy54Qi5oKG0sYTVbal0pCmk9
+YTZbcF0KaD1pLnkKaWYoIShoPT09Mnx8aD09PTN8fGg9PT00fHxoPT09NXx8aT09PW8pKWlmKCEoaT09
+PW4pKWs9ITEKZWxzZSBrPSEwCmVsc2Ugaz0hMAppZighayltKz0iIGV4dGVuZHMgIitILmRtKGksYTUp
+fW0rPSI+In1lbHNle209IiIKcj1udWxsfW89YTQuegpnPWE0LlEKZj1nLmEKZT1mLmxlbmd0aApkPWcu
+YgpjPWQubGVuZ3RoCmI9Zy5jCmE9Yi5sZW5ndGgKYTA9SC5kbShvLGE1KQpmb3IoYTE9IiIsYTI9IiIs
+cD0wO3A8ZTsrK3AsYTI9YTMpYTErPWEyK0guZG0oZltwXSxhNSkKaWYoYz4wKXthMSs9YTIrIlsiCmZv
+cihhMj0iIixwPTA7cDxjOysrcCxhMj1hMylhMSs9YTIrSC5kbShkW3BdLGE1KQphMSs9Il0ifWlmKGE+
+MCl7YTErPWEyKyJ7Igpmb3IoYTI9IiIscD0wO3A8YTtwKz0zLGEyPWEzKXthMSs9YTIKaWYoYltwKzFd
+KWExKz0icmVxdWlyZWQgIgphMSs9SC5kbShiW3ArMl0sYTUpKyIgIitiW3BdfWExKz0ifSJ9aWYociE9
+bnVsbCl7YTUudG9TdHJpbmcKYTUubGVuZ3RoPXJ9cmV0dXJuIG0rIigiK2ExKyIpID0+ICIrYTB9LApk
+bTpmdW5jdGlvbihhLGIpe3ZhciBzLHIscSxwLG8sbixtLGw9YS55CmlmKGw9PT01KXJldHVybiJlcmFz
+ZWQiCmlmKGw9PT0yKXJldHVybiJkeW5hbWljIgppZihsPT09MylyZXR1cm4idm9pZCIKaWYobD09PTEp
+cmV0dXJuIk5ldmVyIgppZihsPT09NClyZXR1cm4iYW55IgppZihsPT09Nil7cz1ILmRtKGEueixiKQpy
+ZXR1cm4gc31pZihsPT09Nyl7cj1hLnoKcz1ILmRtKHIsYikKcT1yLnkKcmV0dXJuKHE9PT0xMXx8cT09
+PTEyPyIoIitzKyIpIjpzKSsiPyJ9aWYobD09PTgpcmV0dXJuIkZ1dHVyZU9yPCIrSC5kbShhLnosYikr
+Ij4iCmlmKGw9PT05KXtwPUgubzMoYS56KQpvPWEuUQpyZXR1cm4gby5sZW5ndGghPT0wP3ArKCI8IitI
+LmlvKG8sYikrIj4iKTpwfWlmKGw9PT0xMSlyZXR1cm4gSC5iSShhLGIsbnVsbCkKaWYobD09PTEyKXJl
+dHVybiBILmJJKGEueixiLGEuUSkKaWYobD09PTEzKXtuPWEuegptPWIubGVuZ3RoCm49bS0xLW4KaWYo
+bjwwfHxuPj1tKXJldHVybiBILk9IKGIsbikKcmV0dXJuIGJbbl19cmV0dXJuIj8ifSwKbzM6ZnVuY3Rp
+b24oYSl7dmFyIHMscj12Lm1hbmdsZWRHbG9iYWxOYW1lc1thXQppZihyIT1udWxsKXJldHVybiByCnM9
+Im1pbmlmaWVkOiIrYQpyZXR1cm4gc30sClFvOmZ1bmN0aW9uKGEsYil7dmFyIHM9YS50UltiXQpmb3Io
+O3R5cGVvZiBzPT0ic3RyaW5nIjspcz1hLnRSW3NdCnJldHVybiBzfSwKYWk6ZnVuY3Rpb24oYSxiKXt2
+YXIgcyxyLHEscCxvLG49YS5lVCxtPW5bYl0KaWYobT09bnVsbClyZXR1cm4gSC5FKGEsYiwhMSkKZWxz
+ZSBpZih0eXBlb2YgbT09Im51bWJlciIpe3M9bQpyPUgubShhLDUsIiMiKQpxPVtdCmZvcihwPTA7cDxz
+OysrcClxLnB1c2gocikKbz1ILkooYSxiLHEpCm5bYl09bwpyZXR1cm4gb31lbHNlIHJldHVybiBtfSwK
+eGI6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gSC5JeChhLnRSLGIpfSwKRkY6ZnVuY3Rpb24oYSxiKXtyZXR1
+cm4gSC5JeChhLmVULGIpfSwKRTpmdW5jdGlvbihhLGIsYyl7dmFyIHMscj1hLmVDLHE9ci5nZXQoYikK
+aWYocSE9bnVsbClyZXR1cm4gcQpzPUguaShILm8oYSxudWxsLGIsYykpCnIuc2V0KGIscykKcmV0dXJu
+IHN9LApjRTpmdW5jdGlvbihhLGIsYyl7dmFyIHMscixxPWIuY2gKaWYocT09bnVsbClxPWIuY2g9bmV3
+IE1hcCgpCnM9cS5nZXQoYykKaWYocyE9bnVsbClyZXR1cm4gcwpyPUguaShILm8oYSxiLGMsITApKQpx
+LnNldChjLHIpCnJldHVybiByfSwKdjU6ZnVuY3Rpb24oYSxiLGMpe3ZhciBzLHIscSxwPWIuY3gKaWYo
+cD09bnVsbClwPWIuY3g9bmV3IE1hcCgpCnM9Yy5jeQpyPXAuZ2V0KHMpCmlmKHIhPW51bGwpcmV0dXJu
+IHIKcT1ILmEoYSxiLGMueT09PTEwP2MuUTpbY10pCnAuc2V0KHMscSkKcmV0dXJuIHF9LApCRDpmdW5j
+dGlvbihhLGIpe2IuYT1ILkF1CmIuYj1ILkpKCnJldHVybiBifSwKbTpmdW5jdGlvbihhLGIsYyl7dmFy
+IHMscixxPWEuZUMuZ2V0KGMpCmlmKHEhPW51bGwpcmV0dXJuIHEKcz1uZXcgSC5KYyhudWxsLG51bGwp
+CnMueT1iCnMuY3k9YwpyPUguQkQoYSxzKQphLmVDLnNldChjLHIpCnJldHVybiByfSwKQzpmdW5jdGlv
+bihhLGIsYyl7dmFyIHMscj1iLmN5KyIqIixxPWEuZUMuZ2V0KHIpCmlmKHEhPW51bGwpcmV0dXJuIHEK
+cz1ILlo3KGEsYixyLGMpCmEuZUMuc2V0KHIscykKcmV0dXJuIHN9LApaNzpmdW5jdGlvbihhLGIsYyxk
+KXt2YXIgcyxyLHEKaWYoZCl7cz1iLnkKaWYoIUguQTgoYikpcj1iPT09dC5QfHxiPT09dC5UfHxzPT09
+N3x8cz09PTYKZWxzZSByPSEwCmlmKHIpcmV0dXJuIGJ9cT1uZXcgSC5KYyhudWxsLG51bGwpCnEueT02
+CnEuej1iCnEuY3k9YwpyZXR1cm4gSC5CRChhLHEpfSwKQjpmdW5jdGlvbihhLGIsYyl7dmFyIHMscj1i
+LmN5KyI/IixxPWEuZUMuZ2V0KHIpCmlmKHEhPW51bGwpcmV0dXJuIHEKcz1ILmxsKGEsYixyLGMpCmEu
+ZUMuc2V0KHIscykKcmV0dXJuIHN9LApsbDpmdW5jdGlvbihhLGIsYyxkKXt2YXIgcyxyLHEscAppZihk
+KXtzPWIueQppZighSC5BOChiKSlpZighKGI9PT10LlB8fGI9PT10LlQpKWlmKHMhPT03KXI9cz09PTgm
+JkgubFIoYi56KQplbHNlIHI9ITAKZWxzZSByPSEwCmVsc2Ugcj0hMAppZihyKXJldHVybiBiCmVsc2Ug
+aWYocz09PTF8fGI9PT10LmF3KXJldHVybiB0LlAKZWxzZSBpZihzPT09Nil7cT1iLnoKaWYocS55PT09
+OCYmSC5sUihxLnopKXJldHVybiBxCmVsc2UgcmV0dXJuIEguY3ooYSxiKX19cD1uZXcgSC5KYyhudWxs
+LG51bGwpCnAueT03CnAuej1iCnAuY3k9YwpyZXR1cm4gSC5CRChhLHApfSwKZjpmdW5jdGlvbihhLGIs
+Yyl7dmFyIHMscj1iLmN5KyIvIixxPWEuZUMuZ2V0KHIpCmlmKHEhPW51bGwpcmV0dXJuIHEKcz1ILmVW
+KGEsYixyLGMpCmEuZUMuc2V0KHIscykKcmV0dXJuIHN9LAplVjpmdW5jdGlvbihhLGIsYyxkKXt2YXIg
+cyxyLHEKaWYoZCl7cz1iLnkKaWYoIUguQTgoYikpaWYoIShiPT09dC5fKSlyPSExCmVsc2Ugcj0hMApl
bHNlIHI9ITAKaWYocnx8Yj09PXQuSylyZXR1cm4gYgplbHNlIGlmKHM9PT0xKXJldHVybiBILkooYSwi
YjgiLFtiXSkKZWxzZSBpZihiPT09dC5QfHxiPT09dC5UKXJldHVybiB0LmJHfXE9bmV3IEguSmMobnVs
bCxudWxsKQpxLnk9OApxLno9YgpxLmN5PWMKcmV0dXJuIEguQkQoYSxxKX0sCkg6ZnVuY3Rpb24oYSxi
@@ -8347,1290 +8322,1282 @@
cT4wKXtuPUguUEwoYSxiLHIsMCkKbT1ILmJaKGEsYyxyLDApCnJldHVybiBILkQoYSxuLG0sYyE9PW0p
fX1sPW5ldyBILkpjKG51bGwsbnVsbCkKbC55PTEyCmwuej1iCmwuUT1jCmwuY3k9ZApyZXR1cm4gSC5C
RChhLGwpfSwKbzpmdW5jdGlvbihhLGIsYyxkKXtyZXR1cm57dTphLGU6YixyOmMsczpbXSxwOjAsbjpk
-fX0sCmk6ZnVuY3Rpb24oYSl7dmFyIHMscixxLHAsbyxuLG0sbCxrLGosaSxoLGc9YS5yLGY9YS5zCmZv
-cihzPWcubGVuZ3RoLHI9MDtyPHM7KXtxPWcuY2hhckNvZGVBdChyKQppZihxPj00OCYmcTw9NTcpcj1I
-LkEocisxLHEsZyxmKQplbHNlIGlmKCgoKHF8MzIpPj4+MCktOTcmNjU1MzUpPDI2fHxxPT09OTV8fHE9
-PT0zNilyPUgudChhLHIsZyxmLCExKQplbHNlIGlmKHE9PT00NilyPUgudChhLHIsZyxmLCEwKQplbHNl
-eysrcgpzd2l0Y2gocSl7Y2FzZSA0NDpicmVhawpjYXNlIDU4OmYucHVzaCghMSkKYnJlYWsKY2FzZSAz
-MzpmLnB1c2goITApCmJyZWFrCmNhc2UgNTk6Zi5wdXNoKEguSyhhLnUsYS5lLGYucG9wKCkpKQpicmVh
-awpjYXNlIDk0OmYucHVzaChILkgoYS51LGYucG9wKCkpKQpicmVhawpjYXNlIDM1OmYucHVzaChILm0o
-YS51LDUsIiMiKSkKYnJlYWsKY2FzZSA2NDpmLnB1c2goSC5tKGEudSwyLCJAIikpCmJyZWFrCmNhc2Ug
-MTI2OmYucHVzaChILm0oYS51LDMsIn4iKSkKYnJlYWsKY2FzZSA2MDpmLnB1c2goYS5wKQphLnA9Zi5s
-ZW5ndGgKYnJlYWsKY2FzZSA2MjpwPWEudQpvPWYuc3BsaWNlKGEucCkKSC5yKGEudSxhLmUsbykKYS5w
-PWYucG9wKCkKbj1mLnBvcCgpCmlmKHR5cGVvZiBuPT0ic3RyaW5nIilmLnB1c2goSC5KKHAsbixvKSkK
-ZWxzZXttPUguSyhwLGEuZSxuKQpzd2l0Y2gobS55KXtjYXNlIDExOmYucHVzaChILkQocCxtLG8sYS5u
-KSkKYnJlYWsKZGVmYXVsdDpmLnB1c2goSC5hKHAsbSxvKSkKYnJlYWt9fWJyZWFrCmNhc2UgMzg6SC5J
-KGEsZikKYnJlYWsKY2FzZSA0MjpsPWEudQpmLnB1c2goSC5DKGwsSC5LKGwsYS5lLGYucG9wKCkpLGEu
-bikpCmJyZWFrCmNhc2UgNjM6bD1hLnUKZi5wdXNoKEguQihsLEguSyhsLGEuZSxmLnBvcCgpKSxhLm4p
-KQpicmVhawpjYXNlIDQ3Omw9YS51CmYucHVzaChILmYobCxILksobCxhLmUsZi5wb3AoKSksYS5uKSkK
-YnJlYWsKY2FzZSA0MDpmLnB1c2goYS5wKQphLnA9Zi5sZW5ndGgKYnJlYWsKY2FzZSA0MTpwPWEudQpr
-PW5ldyBILkcoKQpqPXAuc0VBCmk9cC5zRUEKbj1mLnBvcCgpCmlmKHR5cGVvZiBuPT0ibnVtYmVyIilz
-d2l0Y2gobil7Y2FzZS0xOmo9Zi5wb3AoKQpicmVhawpjYXNlLTI6aT1mLnBvcCgpCmJyZWFrCmRlZmF1
-bHQ6Zi5wdXNoKG4pCmJyZWFrfWVsc2UgZi5wdXNoKG4pCm89Zi5zcGxpY2UoYS5wKQpILnIoYS51LGEu
-ZSxvKQphLnA9Zi5wb3AoKQprLmE9bwprLmI9agprLmM9aQpmLnB1c2goSC5kKHAsSC5LKHAsYS5lLGYu
-cG9wKCkpLGspKQpicmVhawpjYXNlIDkxOmYucHVzaChhLnApCmEucD1mLmxlbmd0aApicmVhawpjYXNl
-IDkzOm89Zi5zcGxpY2UoYS5wKQpILnIoYS51LGEuZSxvKQphLnA9Zi5wb3AoKQpmLnB1c2gobykKZi5w
-dXNoKC0xKQpicmVhawpjYXNlIDEyMzpmLnB1c2goYS5wKQphLnA9Zi5sZW5ndGgKYnJlYWsKY2FzZSAx
-MjU6bz1mLnNwbGljZShhLnApCkgueShhLnUsYS5lLG8pCmEucD1mLnBvcCgpCmYucHVzaChvKQpmLnB1
-c2goLTIpCmJyZWFrCmRlZmF1bHQ6dGhyb3ciQmFkIGNoYXJhY3RlciAiK3F9fX1oPWYucG9wKCkKcmV0
-dXJuIEguSyhhLnUsYS5lLGgpfSwKQTpmdW5jdGlvbihhLGIsYyxkKXt2YXIgcyxyLHE9Yi00OApmb3Io
-cz1jLmxlbmd0aDthPHM7KythKXtyPWMuY2hhckNvZGVBdChhKQppZighKHI+PTQ4JiZyPD01NykpYnJl
-YWsKcT1xKjEwKyhyLTQ4KX1kLnB1c2gocSkKcmV0dXJuIGF9LAp0OmZ1bmN0aW9uKGEsYixjLGQsZSl7
-dmFyIHMscixxLHAsbyxuLG09YisxCmZvcihzPWMubGVuZ3RoO208czsrK20pe3I9Yy5jaGFyQ29kZUF0
-KG0pCmlmKHI9PT00Nil7aWYoZSlicmVhawplPSEwfWVsc2V7aWYoISgoKChyfDMyKT4+PjApLTk3JjY1
-NTM1KTwyNnx8cj09PTk1fHxyPT09MzYpKXE9cj49NDgmJnI8PTU3CmVsc2UgcT0hMAppZighcSlicmVh
-a319cD1jLnN1YnN0cmluZyhiLG0pCmlmKGUpe3M9YS51Cm89YS5lCmlmKG8ueT09PTEwKW89by56Cm49
-SC5RbyhzLG8ueilbcF0KaWYobj09bnVsbClILnYoJ05vICInK3ArJyIgaW4gIicrSC5tRChvKSsnIicp
-CmQucHVzaChILmNFKHMsbyxuKSl9ZWxzZSBkLnB1c2gocCkKcmV0dXJuIG19LApJOmZ1bmN0aW9uKGEs
-Yil7dmFyIHM9Yi5wb3AoKQppZigwPT09cyl7Yi5wdXNoKEgubShhLnUsMSwiMCYiKSkKcmV0dXJufWlm
-KDE9PT1zKXtiLnB1c2goSC5tKGEudSw0LCIxJiIpKQpyZXR1cm59dGhyb3cgSC5iKFAuaFYoIlVuZXhw
-ZWN0ZWQgZXh0ZW5kZWQgb3BlcmF0aW9uICIrSC5FaihzKSkpfSwKSzpmdW5jdGlvbihhLGIsYyl7aWYo
-dHlwZW9mIGM9PSJzdHJpbmciKXJldHVybiBILkooYSxjLGEuc0VBKQplbHNlIGlmKHR5cGVvZiBjPT0i
-bnVtYmVyIilyZXR1cm4gSC5UVihhLGIsYykKZWxzZSByZXR1cm4gY30sCnI6ZnVuY3Rpb24oYSxiLGMp
-e3ZhciBzLHI9Yy5sZW5ndGgKZm9yKHM9MDtzPHI7KytzKWNbc109SC5LKGEsYixjW3NdKX0sCnk6ZnVu
-Y3Rpb24oYSxiLGMpe3ZhciBzLHI9Yy5sZW5ndGgKZm9yKHM9MjtzPHI7cys9MyljW3NdPUguSyhhLGIs
-Y1tzXSl9LApUVjpmdW5jdGlvbihhLGIsYyl7dmFyIHMscixxPWIueQppZihxPT09MTApe2lmKGM9PT0w
-KXJldHVybiBiLnoKcz1iLlEKcj1zLmxlbmd0aAppZihjPD1yKXJldHVybiBzW2MtMV0KYy09cgpiPWIu
-egpxPWIueX1lbHNlIGlmKGM9PT0wKXJldHVybiBiCmlmKHEhPT05KXRocm93IEguYihQLmhWKCJJbmRl
-eGVkIGJhc2UgbXVzdCBiZSBhbiBpbnRlcmZhY2UgdHlwZSIpKQpzPWIuUQppZihjPD1zLmxlbmd0aCly
-ZXR1cm4gc1tjLTFdCnRocm93IEguYihQLmhWKCJCYWQgaW5kZXggIitjKyIgZm9yICIrYi53KDApKSl9
-LApXZTpmdW5jdGlvbihhLGIsYyxkLGUpe3ZhciBzLHIscSxwLG8sbixtLGwsayxqCmlmKGI9PT1kKXJl
-dHVybiEwCmlmKCFILkE4KGQpKWlmKCEoZD09PXQuXykpcz1kPT09dC5LCmVsc2Ugcz0hMAplbHNlIHM9
-ITAKaWYocylyZXR1cm4hMApyPWIueQppZihyPT09NClyZXR1cm4hMAppZihILkE4KGIpKXJldHVybiEx
-CmlmKGIueSE9PTEpcz1iPT09dC5QfHxiPT09dC5UCmVsc2Ugcz0hMAppZihzKXJldHVybiEwCnE9cj09
-PTEzCmlmKHEpaWYoSC5XZShhLGNbYi56XSxjLGQsZSkpcmV0dXJuITAKcD1kLnkKaWYocj09PTYpcmV0
-dXJuIEguV2UoYSxiLnosYyxkLGUpCmlmKHA9PT02KXtzPWQuegpyZXR1cm4gSC5XZShhLGIsYyxzLGUp
-fWlmKHI9PT04KXtpZighSC5XZShhLGIueixjLGQsZSkpcmV0dXJuITEKcmV0dXJuIEguV2UoYSxILnha
-KGEsYiksYyxkLGUpfWlmKHI9PT03KXtzPUguV2UoYSxiLnosYyxkLGUpCnJldHVybiBzfWlmKHA9PT04
-KXtpZihILldlKGEsYixjLGQueixlKSlyZXR1cm4hMApyZXR1cm4gSC5XZShhLGIsYyxILnhaKGEsZCks
-ZSl9aWYocD09PTcpe3M9SC5XZShhLGIsYyxkLnosZSkKcmV0dXJuIHN9aWYocSlyZXR1cm4hMQpzPXIh
-PT0xMQppZigoIXN8fHI9PT0xMikmJmQ9PT10LlkpcmV0dXJuITAKaWYocD09PTEyKXtpZihiPT09dC54
-KXJldHVybiEwCmlmKHIhPT0xMilyZXR1cm4hMQpvPWIuUQpuPWQuUQptPW8ubGVuZ3RoCmlmKG0hPT1u
-Lmxlbmd0aClyZXR1cm4hMQpjPWM9PW51bGw/bzpvLmNvbmNhdChjKQplPWU9PW51bGw/bjpuLmNvbmNh
-dChlKQpmb3IobD0wO2w8bTsrK2wpe2s9b1tsXQpqPW5bbF0KaWYoIUguV2UoYSxrLGMsaixlKXx8IUgu
-V2UoYSxqLGUsayxjKSlyZXR1cm4hMX1yZXR1cm4gSC5iTyhhLGIueixjLGQueixlKX1pZihwPT09MTEp
-e2lmKGI9PT10LngpcmV0dXJuITAKaWYocylyZXR1cm4hMQpyZXR1cm4gSC5iTyhhLGIsYyxkLGUpfWlm
-KHI9PT05KXtpZihwIT09OSlyZXR1cm4hMQpyZXR1cm4gSC5wRyhhLGIsYyxkLGUpfXJldHVybiExfSwK
-Yk86ZnVuY3Rpb24oYTIsYTMsYTQsYTUsYTYpe3ZhciBzLHIscSxwLG8sbixtLGwsayxqLGksaCxnLGYs
-ZSxkLGMsYixhLGEwLGExCmlmKCFILldlKGEyLGEzLnosYTQsYTUueixhNikpcmV0dXJuITEKcz1hMy5R
-CnI9YTUuUQpxPXMuYQpwPXIuYQpvPXEubGVuZ3RoCm49cC5sZW5ndGgKaWYobz5uKXJldHVybiExCm09
-bi1vCmw9cy5iCms9ci5iCmo9bC5sZW5ndGgKaT1rLmxlbmd0aAppZihvK2o8bitpKXJldHVybiExCmZv
-cihoPTA7aDxvOysraCl7Zz1xW2hdCmlmKCFILldlKGEyLHBbaF0sYTYsZyxhNCkpcmV0dXJuITF9Zm9y
-KGg9MDtoPG07KytoKXtnPWxbaF0KaWYoIUguV2UoYTIscFtvK2hdLGE2LGcsYTQpKXJldHVybiExfWZv
-cihoPTA7aDxpOysraCl7Zz1sW20raF0KaWYoIUguV2UoYTIsa1toXSxhNixnLGE0KSlyZXR1cm4hMX1m
-PXMuYwplPXIuYwpkPWYubGVuZ3RoCmM9ZS5sZW5ndGgKZm9yKGI9MCxhPTA7YTxjO2ErPTMpe2EwPWVb
-YV0KZm9yKDshMDspe2lmKGI+PWQpcmV0dXJuITEKYTE9ZltiXQpiKz0zCmlmKGEwPGExKXJldHVybiEx
-CmlmKGExPGEwKWNvbnRpbnVlCmc9ZltiLTFdCmlmKCFILldlKGEyLGVbYSsyXSxhNixnLGE0KSlyZXR1
-cm4hMQpicmVha319cmV0dXJuITB9LApwRzpmdW5jdGlvbihhLGIsYyxkLGUpe3ZhciBzLHIscSxwLG8s
-bixtLGwsaz1iLnosaj1kLnoKaWYoaz09PWope3M9Yi5RCnI9ZC5RCnE9cy5sZW5ndGgKZm9yKHA9MDtw
-PHE7KytwKXtvPXNbcF0Kbj1yW3BdCmlmKCFILldlKGEsbyxjLG4sZSkpcmV0dXJuITF9cmV0dXJuITB9
-aWYoZD09PXQuSylyZXR1cm4hMAptPUguUW8oYSxrKQppZihtPT1udWxsKXJldHVybiExCmw9bVtqXQpp
-ZihsPT1udWxsKXJldHVybiExCnE9bC5sZW5ndGgKcj1kLlEKZm9yKHA9MDtwPHE7KytwKWlmKCFILldl
-KGEsSC5jRShhLGIsbFtwXSksYyxyW3BdLGUpKXJldHVybiExCnJldHVybiEwfSwKbFI6ZnVuY3Rpb24o
-YSl7dmFyIHMscj1hLnkKaWYoIShhPT09dC5QfHxhPT09dC5UKSlpZighSC5BOChhKSlpZihyIT09Nylp
-ZighKHI9PT02JiZILmxSKGEueikpKXM9cj09PTgmJkgubFIoYS56KQplbHNlIHM9ITAKZWxzZSBzPSEw
-CmVsc2Ugcz0hMAplbHNlIHM9ITAKcmV0dXJuIHN9LApjYzpmdW5jdGlvbihhKXt2YXIgcwppZighSC5B
-OChhKSlpZighKGE9PT10Ll8pKXM9YT09PXQuSwplbHNlIHM9ITAKZWxzZSBzPSEwCnJldHVybiBzfSwK
+fX0sCmk6ZnVuY3Rpb24oYSl7dmFyIHMscixxLHAsbyxuLG0sbCxrLGosaSxoPWEucixnPWEucwpmb3Io
+cz1oLmxlbmd0aCxyPTA7cjxzOyl7cT1oLmNoYXJDb2RlQXQocikKaWYocT49NDgmJnE8PTU3KXI9SC5B
+KHIrMSxxLGgsZykKZWxzZSBpZigoKChxfDMyKT4+PjApLTk3JjY1NTM1KTwyNnx8cT09PTk1fHxxPT09
+MzYpcj1ILnQoYSxyLGgsZywhMSkKZWxzZSBpZihxPT09NDYpcj1ILnQoYSxyLGgsZywhMCkKZWxzZXsr
+K3IKc3dpdGNoKHEpe2Nhc2UgNDQ6YnJlYWsKY2FzZSA1ODpnLnB1c2goITEpCmJyZWFrCmNhc2UgMzM6
+Zy5wdXNoKCEwKQpicmVhawpjYXNlIDU5OmcucHVzaChILksoYS51LGEuZSxnLnBvcCgpKSkKYnJlYWsK
+Y2FzZSA5NDpnLnB1c2goSC5IKGEudSxnLnBvcCgpKSkKYnJlYWsKY2FzZSAzNTpnLnB1c2goSC5tKGEu
+dSw1LCIjIikpCmJyZWFrCmNhc2UgNjQ6Zy5wdXNoKEgubShhLnUsMiwiQCIpKQpicmVhawpjYXNlIDEy
+NjpnLnB1c2goSC5tKGEudSwzLCJ+IikpCmJyZWFrCmNhc2UgNjA6Zy5wdXNoKGEucCkKYS5wPWcubGVu
+Z3RoCmJyZWFrCmNhc2UgNjI6cD1hLnUKbz1nLnNwbGljZShhLnApCkgucihhLnUsYS5lLG8pCmEucD1n
+LnBvcCgpCm49Zy5wb3AoKQppZih0eXBlb2Ygbj09InN0cmluZyIpZy5wdXNoKEguSihwLG4sbykpCmVs
+c2V7bT1ILksocCxhLmUsbikKc3dpdGNoKG0ueSl7Y2FzZSAxMTpnLnB1c2goSC5EKHAsbSxvLGEubikp
+CmJyZWFrCmRlZmF1bHQ6Zy5wdXNoKEguYShwLG0sbykpCmJyZWFrfX1icmVhawpjYXNlIDM4OkguSShh
+LGcpCmJyZWFrCmNhc2UgNDI6cD1hLnUKZy5wdXNoKEguQyhwLEguSyhwLGEuZSxnLnBvcCgpKSxhLm4p
+KQpicmVhawpjYXNlIDYzOnA9YS51CmcucHVzaChILkIocCxILksocCxhLmUsZy5wb3AoKSksYS5uKSkK
+YnJlYWsKY2FzZSA0NzpwPWEudQpnLnB1c2goSC5mKHAsSC5LKHAsYS5lLGcucG9wKCkpLGEubikpCmJy
+ZWFrCmNhc2UgNDA6Zy5wdXNoKGEucCkKYS5wPWcubGVuZ3RoCmJyZWFrCmNhc2UgNDE6cD1hLnUKbD1u
+ZXcgSC5HKCkKaz1wLnNFQQpqPXAuc0VBCm49Zy5wb3AoKQppZih0eXBlb2Ygbj09Im51bWJlciIpc3dp
+dGNoKG4pe2Nhc2UtMTprPWcucG9wKCkKYnJlYWsKY2FzZS0yOmo9Zy5wb3AoKQpicmVhawpkZWZhdWx0
+OmcucHVzaChuKQpicmVha31lbHNlIGcucHVzaChuKQpvPWcuc3BsaWNlKGEucCkKSC5yKGEudSxhLmUs
+bykKYS5wPWcucG9wKCkKbC5hPW8KbC5iPWsKbC5jPWoKZy5wdXNoKEguZChwLEguSyhwLGEuZSxnLnBv
+cCgpKSxsKSkKYnJlYWsKY2FzZSA5MTpnLnB1c2goYS5wKQphLnA9Zy5sZW5ndGgKYnJlYWsKY2FzZSA5
+MzpvPWcuc3BsaWNlKGEucCkKSC5yKGEudSxhLmUsbykKYS5wPWcucG9wKCkKZy5wdXNoKG8pCmcucHVz
+aCgtMSkKYnJlYWsKY2FzZSAxMjM6Zy5wdXNoKGEucCkKYS5wPWcubGVuZ3RoCmJyZWFrCmNhc2UgMTI1
+Om89Zy5zcGxpY2UoYS5wKQpILnkoYS51LGEuZSxvKQphLnA9Zy5wb3AoKQpnLnB1c2gobykKZy5wdXNo
+KC0yKQpicmVhawpkZWZhdWx0OnRocm93IkJhZCBjaGFyYWN0ZXIgIitxfX19aT1nLnBvcCgpCnJldHVy
+biBILksoYS51LGEuZSxpKX0sCkE6ZnVuY3Rpb24oYSxiLGMsZCl7dmFyIHMscixxPWItNDgKZm9yKHM9
+Yy5sZW5ndGg7YTxzOysrYSl7cj1jLmNoYXJDb2RlQXQoYSkKaWYoIShyPj00OCYmcjw9NTcpKWJyZWFr
+CnE9cSoxMCsoci00OCl9ZC5wdXNoKHEpCnJldHVybiBhfSwKdDpmdW5jdGlvbihhLGIsYyxkLGUpe3Zh
+ciBzLHIscSxwLG8sbixtPWIrMQpmb3Iocz1jLmxlbmd0aDttPHM7KyttKXtyPWMuY2hhckNvZGVBdCht
+KQppZihyPT09NDYpe2lmKGUpYnJlYWsKZT0hMH1lbHNle2lmKCEoKCgocnwzMik+Pj4wKS05NyY2NTUz
+NSk8MjZ8fHI9PT05NXx8cj09PTM2KSlxPXI+PTQ4JiZyPD01NwplbHNlIHE9ITAKaWYoIXEpYnJlYWt9
+fXA9Yy5zdWJzdHJpbmcoYixtKQppZihlKXtzPWEudQpvPWEuZQppZihvLnk9PT0xMClvPW8uegpuPUgu
+UW8ocyxvLnopW3BdCmlmKG49PW51bGwpSC52KCdObyAiJytwKyciIGluICInK0gubUQobykrJyInKQpk
+LnB1c2goSC5jRShzLG8sbikpfWVsc2UgZC5wdXNoKHApCnJldHVybiBtfSwKSTpmdW5jdGlvbihhLGIp
+e3ZhciBzPWIucG9wKCkKaWYoMD09PXMpe2IucHVzaChILm0oYS51LDEsIjAmIikpCnJldHVybn1pZigx
+PT09cyl7Yi5wdXNoKEgubShhLnUsNCwiMSYiKSkKcmV0dXJufXRocm93IEguYihQLmhWKCJVbmV4cGVj
+dGVkIGV4dGVuZGVkIG9wZXJhdGlvbiAiK0guRWoocykpKX0sCks6ZnVuY3Rpb24oYSxiLGMpe2lmKHR5
+cGVvZiBjPT0ic3RyaW5nIilyZXR1cm4gSC5KKGEsYyxhLnNFQSkKZWxzZSBpZih0eXBlb2YgYz09Im51
+bWJlciIpcmV0dXJuIEguVFYoYSxiLGMpCmVsc2UgcmV0dXJuIGN9LApyOmZ1bmN0aW9uKGEsYixjKXt2
+YXIgcyxyPWMubGVuZ3RoCmZvcihzPTA7czxyOysrcyljW3NdPUguSyhhLGIsY1tzXSl9LAp5OmZ1bmN0
+aW9uKGEsYixjKXt2YXIgcyxyPWMubGVuZ3RoCmZvcihzPTI7czxyO3MrPTMpY1tzXT1ILksoYSxiLGNb
+c10pfSwKVFY6ZnVuY3Rpb24oYSxiLGMpe3ZhciBzLHIscT1iLnkKaWYocT09PTEwKXtpZihjPT09MCly
+ZXR1cm4gYi56CnM9Yi5RCnI9cy5sZW5ndGgKaWYoYzw9cilyZXR1cm4gc1tjLTFdCmMtPXIKYj1iLnoK
+cT1iLnl9ZWxzZSBpZihjPT09MClyZXR1cm4gYgppZihxIT09OSl0aHJvdyBILmIoUC5oVigiSW5kZXhl
+ZCBiYXNlIG11c3QgYmUgYW4gaW50ZXJmYWNlIHR5cGUiKSkKcz1iLlEKaWYoYzw9cy5sZW5ndGgpcmV0
+dXJuIHNbYy0xXQp0aHJvdyBILmIoUC5oVigiQmFkIGluZGV4ICIrYysiIGZvciAiK2IudygwKSkpfSwK
+V2U6ZnVuY3Rpb24oYSxiLGMsZCxlKXt2YXIgcyxyLHEscCxvLG4sbSxsLGssagppZihiPT09ZClyZXR1
+cm4hMAppZighSC5BOChkKSlpZighKGQ9PT10Ll8pKXM9ITEKZWxzZSBzPSEwCmVsc2Ugcz0hMAppZihz
+KXJldHVybiEwCnI9Yi55CmlmKHI9PT00KXJldHVybiEwCmlmKEguQTgoYikpcmV0dXJuITEKaWYoYi55
+IT09MSlzPSExCmVsc2Ugcz0hMAppZihzKXJldHVybiEwCnE9cj09PTEzCmlmKHEpaWYoSC5XZShhLGNb
+Yi56XSxjLGQsZSkpcmV0dXJuITAKcD1kLnkKcz1iPT09dC5QfHxiPT09dC5UCmlmKHMpe2lmKHA9PT04
+KXJldHVybiBILldlKGEsYixjLGQueixlKQpyZXR1cm4gZD09PXQuUHx8ZD09PXQuVHx8cD09PTd8fHA9
+PT02fWlmKGQ9PT10Lkspe2lmKHI9PT04KXJldHVybiBILldlKGEsYi56LGMsZCxlKQppZihyPT09Nily
+ZXR1cm4gSC5XZShhLGIueixjLGQsZSkKcmV0dXJuIHIhPT03fWlmKHI9PT02KXJldHVybiBILldlKGEs
+Yi56LGMsZCxlKQppZihwPT09Nil7cz1ILmN6KGEsZCkKcmV0dXJuIEguV2UoYSxiLGMscyxlKX1pZihy
+PT09OCl7aWYoIUguV2UoYSxiLnosYyxkLGUpKXJldHVybiExCnJldHVybiBILldlKGEsSC54WihhLGIp
+LGMsZCxlKX1pZihyPT09Nyl7cz1ILldlKGEsdC5QLGMsZCxlKQpyZXR1cm4gcyYmSC5XZShhLGIueixj
+LGQsZSl9aWYocD09PTgpe2lmKEguV2UoYSxiLGMsZC56LGUpKXJldHVybiEwCnJldHVybiBILldlKGEs
+YixjLEgueFooYSxkKSxlKX1pZihwPT09Nyl7cz1ILldlKGEsYixjLHQuUCxlKQpyZXR1cm4gc3x8SC5X
+ZShhLGIsYyxkLnosZSl9aWYocSlyZXR1cm4hMQpzPXIhPT0xMQppZigoIXN8fHI9PT0xMikmJmQ9PT10
+LlkpcmV0dXJuITAKaWYocD09PTEyKXtpZihiPT09dC51KXJldHVybiEwCmlmKHIhPT0xMilyZXR1cm4h
+MQpvPWIuUQpuPWQuUQptPW8ubGVuZ3RoCmlmKG0hPT1uLmxlbmd0aClyZXR1cm4hMQpjPWM9PW51bGw/
+bzpvLmNvbmNhdChjKQplPWU9PW51bGw/bjpuLmNvbmNhdChlKQpmb3IobD0wO2w8bTsrK2wpe2s9b1ts
+XQpqPW5bbF0KaWYoIUguV2UoYSxrLGMsaixlKXx8IUguV2UoYSxqLGUsayxjKSlyZXR1cm4hMX1yZXR1
+cm4gSC5iTyhhLGIueixjLGQueixlKX1pZihwPT09MTEpe2lmKGI9PT10LnUpcmV0dXJuITAKaWYocyly
+ZXR1cm4hMQpyZXR1cm4gSC5iTyhhLGIsYyxkLGUpfWlmKHI9PT05KXtpZihwIT09OSlyZXR1cm4hMQpy
+ZXR1cm4gSC5wRyhhLGIsYyxkLGUpfXJldHVybiExfSwKYk86ZnVuY3Rpb24oYTMsYTQsYTUsYTYsYTcp
+e3ZhciBzLHIscSxwLG8sbixtLGwsayxqLGksaCxnLGYsZSxkLGMsYixhLGEwLGExLGEyCmlmKCFILldl
+KGEzLGE0LnosYTUsYTYueixhNykpcmV0dXJuITEKcz1hNC5RCnI9YTYuUQpxPXMuYQpwPXIuYQpvPXEu
+bGVuZ3RoCm49cC5sZW5ndGgKaWYobz5uKXJldHVybiExCm09bi1vCmw9cy5iCms9ci5iCmo9bC5sZW5n
+dGgKaT1rLmxlbmd0aAppZihvK2o8bitpKXJldHVybiExCmZvcihoPTA7aDxvOysraCl7Zz1xW2hdCmlm
+KCFILldlKGEzLHBbaF0sYTcsZyxhNSkpcmV0dXJuITF9Zm9yKGg9MDtoPG07KytoKXtnPWxbaF0KaWYo
+IUguV2UoYTMscFtvK2hdLGE3LGcsYTUpKXJldHVybiExfWZvcihoPTA7aDxpOysraCl7Zz1sW20raF0K
+aWYoIUguV2UoYTMsa1toXSxhNyxnLGE1KSlyZXR1cm4hMX1mPXMuYwplPXIuYwpkPWYubGVuZ3RoCmM9
+ZS5sZW5ndGgKZm9yKGI9MCxhPTA7YTxjO2ErPTMpe2EwPWVbYV0KZm9yKDshMDspe2lmKGI+PWQpcmV0
+dXJuITEKYTE9ZltiXQpiKz0zCmlmKGEwPGExKXJldHVybiExCmEyPWZbYi0yXQppZihhMTxhMCl7aWYo
+YTIpcmV0dXJuITEKY29udGludWV9Zz1lW2ErMV0KaWYoYTImJiFnKXJldHVybiExCmc9ZltiLTFdCmlm
+KCFILldlKGEzLGVbYSsyXSxhNyxnLGE1KSlyZXR1cm4hMQpicmVha319Zm9yKDtiPGQ7KXtpZihmW2Ir
+MV0pcmV0dXJuITEKYis9M31yZXR1cm4hMH0sCnBHOmZ1bmN0aW9uKGEsYixjLGQsZSl7dmFyIHMscixx
+LHAsbyxuLG0sbCxrPWIueixqPWQuegppZihrPT09ail7cz1iLlEKcj1kLlEKcT1zLmxlbmd0aApmb3Io
+cD0wO3A8cTsrK3Ape289c1twXQpuPXJbcF0KaWYoIUguV2UoYSxvLGMsbixlKSlyZXR1cm4hMX1yZXR1
+cm4hMH1pZihkPT09dC5LKXJldHVybiEwCm09SC5RbyhhLGspCmlmKG09PW51bGwpcmV0dXJuITEKbD1t
+W2pdCmlmKGw9PW51bGwpcmV0dXJuITEKcT1sLmxlbmd0aApyPWQuUQpmb3IocD0wO3A8cTsrK3ApaWYo
+IUguV2UoYSxILmNFKGEsYixsW3BdKSxjLHJbcF0sZSkpcmV0dXJuITEKcmV0dXJuITB9LApsUjpmdW5j
+dGlvbihhKXt2YXIgcyxyPWEueQppZighKGE9PT10LlB8fGE9PT10LlQpKWlmKCFILkE4KGEpKWlmKHIh
+PT03KWlmKCEocj09PTYmJkgubFIoYS56KSkpcz1yPT09OCYmSC5sUihhLnopCmVsc2Ugcz0hMAplbHNl
+IHM9ITAKZWxzZSBzPSEwCmVsc2Ugcz0hMApyZXR1cm4gc30sCmNjOmZ1bmN0aW9uKGEpe3ZhciBzCmlm
+KCFILkE4KGEpKWlmKCEoYT09PXQuXykpcz0hMQplbHNlIHM9ITAKZWxzZSBzPSEwCnJldHVybiBzfSwK
QTg6ZnVuY3Rpb24oYSl7dmFyIHM9YS55CnJldHVybiBzPT09Mnx8cz09PTN8fHM9PT00fHxzPT09NXx8
-YT09PXQuV30sCkl4OmZ1bmN0aW9uKGEsYil7dmFyIHMscixxPU9iamVjdC5rZXlzKGIpLHA9cS5sZW5n
+YT09PXQuWH0sCkl4OmZ1bmN0aW9uKGEsYil7dmFyIHMscixxPU9iamVjdC5rZXlzKGIpLHA9cS5sZW5n
dGgKZm9yKHM9MDtzPHA7KytzKXtyPXFbc10KYVtyXT1iW3JdfX0sCkpjOmZ1bmN0aW9uIEpjKGEsYil7
dmFyIF89dGhpcwpfLmE9YQpfLmI9YgpfLng9Xy5yPV8uYz1udWxsCl8ueT0wCl8uY3k9Xy5jeD1fLmNo
-PV8uUT1fLno9bnVsbH0sCkc6ZnVuY3Rpb24gRygpe3RoaXMuYz10aGlzLmI9dGhpcy5hPW51bGx9LAps
-WTpmdW5jdGlvbiBsWShhKXt0aGlzLmE9YX0sCmtTOmZ1bmN0aW9uIGtTKCl7fSwKaU06ZnVuY3Rpb24g
-aU0oYSl7dGhpcy5hPWF9LApSOTpmdW5jdGlvbihhKXtyZXR1cm4gdC53LmIoYSl8fHQuQi5iKGEpfHx0
-LmR6LmIoYSl8fHQuSS5iKGEpfHx0LkEuYihhKXx8dC5nNC5iKGEpfHx0LmcyLmIoYSl9LApGUDpmdW5j
-dGlvbihhKXtyZXR1cm4gSC52KEguaihhKSl9fSxKPXsKUXU6ZnVuY3Rpb24oYSxiLGMsZCl7cmV0dXJu
-e2k6YSxwOmIsZTpjLHg6ZH19LAprczpmdW5jdGlvbihhKXt2YXIgcyxyLHEscCxvLG49YVt2LmRpc3Bh
-dGNoUHJvcGVydHlOYW1lXQppZihuPT1udWxsKWlmKCQuQnY9PW51bGwpe0guWEQoKQpuPWFbdi5kaXNw
-YXRjaFByb3BlcnR5TmFtZV19aWYobiE9bnVsbCl7cz1uLnAKaWYoITE9PT1zKXJldHVybiBuLmkKaWYo
-ITA9PT1zKXJldHVybiBhCnI9T2JqZWN0LmdldFByb3RvdHlwZU9mKGEpCmlmKHM9PT1yKXJldHVybiBu
-LmkKaWYobi5lPT09cil0aHJvdyBILmIoUC5TWSgiUmV0dXJuIGludGVyY2VwdG9yIGZvciAiK0guRWoo
-cyhhLG4pKSkpfXE9YS5jb25zdHJ1Y3RvcgppZihxPT1udWxsKXA9bnVsbAplbHNle289JC56bQppZihv
-PT1udWxsKW89JC56bT12LmdldElzb2xhdGVUYWcoIl8kZGFydF9qcyIpCnA9cVtvXX1pZihwIT1udWxs
-KXJldHVybiBwCnA9SC53MyhhKQppZihwIT1udWxsKXJldHVybiBwCmlmKHR5cGVvZiBhPT0iZnVuY3Rp
-b24iKXJldHVybiBDLkRHCnM9T2JqZWN0LmdldFByb3RvdHlwZU9mKGEpCmlmKHM9PW51bGwpcmV0dXJu
-IEMuWlEKaWYocz09PU9iamVjdC5wcm90b3R5cGUpcmV0dXJuIEMuWlEKaWYodHlwZW9mIHE9PSJmdW5j
-dGlvbiIpe289JC56bQppZihvPT1udWxsKW89JC56bT12LmdldElzb2xhdGVUYWcoIl8kZGFydF9qcyIp
-Ck9iamVjdC5kZWZpbmVQcm9wZXJ0eShxLG8se3ZhbHVlOkMudkIsZW51bWVyYWJsZTpmYWxzZSx3cml0
-YWJsZTp0cnVlLGNvbmZpZ3VyYWJsZTp0cnVlfSkKcmV0dXJuIEMudkJ9cmV0dXJuIEMudkJ9LApRaTpm
-dW5jdGlvbihhLGIpe2lmKGE8MHx8YT40Mjk0OTY3Mjk1KXRocm93IEguYihQLlRFKGEsMCw0Mjk0OTY3
-Mjk1LCJsZW5ndGgiLG51bGwpKQpyZXR1cm4gSi5weShuZXcgQXJyYXkoYSksYil9LApLaDpmdW5jdGlv
-bihhLGIpe2lmKGE8MCl0aHJvdyBILmIoUC54WSgiTGVuZ3RoIG11c3QgYmUgYSBub24tbmVnYXRpdmUg
-aW50ZWdlcjogIithKSkKcmV0dXJuIEguUUkobmV3IEFycmF5KGEpLGIuQygiamQ8MD4iKSl9LApweTpm
-dW5jdGlvbihhLGIpe3JldHVybiBKLkVwKEguUUkoYSxiLkMoImpkPDA+IikpLGIpfSwKRXA6ZnVuY3Rp
-b24oYSxiKXthLmZpeGVkJGxlbmd0aD1BcnJheQpyZXR1cm4gYX0sCnpDOmZ1bmN0aW9uKGEpe2EuZml4
-ZWQkbGVuZ3RoPUFycmF5CmEuaW1tdXRhYmxlJGxpc3Q9QXJyYXkKcmV0dXJuIGF9LApHYTpmdW5jdGlv
-bihhKXtpZihhPDI1Nilzd2l0Y2goYSl7Y2FzZSA5OmNhc2UgMTA6Y2FzZSAxMTpjYXNlIDEyOmNhc2Ug
-MTM6Y2FzZSAzMjpjYXNlIDEzMzpjYXNlIDE2MDpyZXR1cm4hMApkZWZhdWx0OnJldHVybiExfXN3aXRj
-aChhKXtjYXNlIDU3NjA6Y2FzZSA4MTkyOmNhc2UgODE5MzpjYXNlIDgxOTQ6Y2FzZSA4MTk1OmNhc2Ug
-ODE5NjpjYXNlIDgxOTc6Y2FzZSA4MTk4OmNhc2UgODE5OTpjYXNlIDgyMDA6Y2FzZSA4MjAxOmNhc2Ug
-ODIwMjpjYXNlIDgyMzI6Y2FzZSA4MjMzOmNhc2UgODIzOTpjYXNlIDgyODc6Y2FzZSAxMjI4ODpjYXNl
-IDY1Mjc5OnJldHVybiEwCmRlZmF1bHQ6cmV0dXJuITF9fSwKbW06ZnVuY3Rpb24oYSxiKXt2YXIgcyxy
-CmZvcihzPWEubGVuZ3RoO2I8czspe3I9Qy54Qi5XKGEsYikKaWYociE9PTMyJiZyIT09MTMmJiFKLkdh
-KHIpKWJyZWFrOysrYn1yZXR1cm4gYn0sCmMxOmZ1bmN0aW9uKGEsYil7dmFyIHMscgpmb3IoO2I+MDti
-PXMpe3M9Yi0xCnI9Qy54Qi5PKGEscykKaWYociE9PTMyJiZyIT09MTMmJiFKLkdhKHIpKWJyZWFrfXJl
-dHVybiBifSwKUWM6ZnVuY3Rpb24oYSl7aWYodHlwZW9mIGE9PSJudW1iZXIiKXJldHVybiBKLnFJLnBy
-b3RvdHlwZQppZih0eXBlb2YgYT09InN0cmluZyIpcmV0dXJuIEouRHIucHJvdG90eXBlCmlmKGE9PW51
-bGwpcmV0dXJuIGEKaWYoIShhIGluc3RhbmNlb2YgUC5NaCkpcmV0dXJuIEoua2QucHJvdG90eXBlCnJl
-dHVybiBhfSwKVTY6ZnVuY3Rpb24oYSl7aWYodHlwZW9mIGE9PSJzdHJpbmciKXJldHVybiBKLkRyLnBy
-b3RvdHlwZQppZihhPT1udWxsKXJldHVybiBhCmlmKGEuY29uc3RydWN0b3I9PUFycmF5KXJldHVybiBK
-LmpkLnByb3RvdHlwZQppZih0eXBlb2YgYSE9Im9iamVjdCIpe2lmKHR5cGVvZiBhPT0iZnVuY3Rpb24i
-KXJldHVybiBKLmM1LnByb3RvdHlwZQpyZXR1cm4gYX1pZihhIGluc3RhbmNlb2YgUC5NaClyZXR1cm4g
-YQpyZXR1cm4gSi5rcyhhKX0sCllFOmZ1bmN0aW9uKGEpe2lmKGE9PW51bGwpcmV0dXJuIGEKaWYodHlw
-ZW9mIGEhPSJvYmplY3QiKXtpZih0eXBlb2YgYT09ImZ1bmN0aW9uIilyZXR1cm4gSi5jNS5wcm90b3R5
-cGUKcmV0dXJuIGF9aWYoYSBpbnN0YW5jZW9mIFAuTWgpcmV0dXJuIGEKcmV0dXJuIEoua3MoYSl9LApp
-YTpmdW5jdGlvbihhKXtpZih0eXBlb2YgYT09Im51bWJlciIpe2lmKE1hdGguZmxvb3IoYSk9PWEpcmV0
-dXJuIEouYlUucHJvdG90eXBlCnJldHVybiBKLmtELnByb3RvdHlwZX1pZih0eXBlb2YgYT09InN0cmlu
-ZyIpcmV0dXJuIEouRHIucHJvdG90eXBlCmlmKGE9PW51bGwpcmV0dXJuIEoud2UucHJvdG90eXBlCmlm
-KHR5cGVvZiBhPT0iYm9vbGVhbiIpcmV0dXJuIEoueUUucHJvdG90eXBlCmlmKGEuY29uc3RydWN0b3I9
-PUFycmF5KXJldHVybiBKLmpkLnByb3RvdHlwZQppZih0eXBlb2YgYSE9Im9iamVjdCIpe2lmKHR5cGVv
-ZiBhPT0iZnVuY3Rpb24iKXJldHVybiBKLmM1LnByb3RvdHlwZQpyZXR1cm4gYX1pZihhIGluc3RhbmNl
-b2YgUC5NaClyZXR1cm4gYQpyZXR1cm4gSi5rcyhhKX0sCnJZOmZ1bmN0aW9uKGEpe2lmKHR5cGVvZiBh
-PT0ic3RyaW5nIilyZXR1cm4gSi5Eci5wcm90b3R5cGUKaWYoYT09bnVsbClyZXR1cm4gYQppZighKGEg
-aW5zdGFuY2VvZiBQLk1oKSlyZXR1cm4gSi5rZC5wcm90b3R5cGUKcmV0dXJuIGF9LAp2ZDpmdW5jdGlv
-bihhKXtpZih0eXBlb2YgYT09Im51bWJlciIpcmV0dXJuIEoucUkucHJvdG90eXBlCmlmKGE9PW51bGwp
-cmV0dXJuIGEKaWYoIShhIGluc3RhbmNlb2YgUC5NaCkpcmV0dXJuIEoua2QucHJvdG90eXBlCnJldHVy
-biBhfSwKdzE6ZnVuY3Rpb24oYSl7aWYoYT09bnVsbClyZXR1cm4gYQppZihhLmNvbnN0cnVjdG9yPT1B
-cnJheSlyZXR1cm4gSi5qZC5wcm90b3R5cGUKaWYodHlwZW9mIGEhPSJvYmplY3QiKXtpZih0eXBlb2Yg
-YT09ImZ1bmN0aW9uIilyZXR1cm4gSi5jNS5wcm90b3R5cGUKcmV0dXJuIGF9aWYoYSBpbnN0YW5jZW9m
-IFAuTWgpcmV0dXJuIGEKcmV0dXJuIEoua3MoYSl9LApBNTpmdW5jdGlvbihhLGIpe3JldHVybiBKLncx
-KGEpLmVSKGEsYil9LApEMTpmdW5jdGlvbihhLGIpe3JldHVybiBKLnJZKGEpLk9ZKGEsYil9LApFaDpm
-dW5jdGlvbihhLGIsYyl7cmV0dXJuIEouWUUoYSkubUsoYSxiLGMpfSwKRWw6ZnVuY3Rpb24oYSxiKXty
-ZXR1cm4gSi53MShhKS5kcihhLGIpfSwKRjc6ZnVuY3Rpb24oYSl7cmV0dXJuIEouVTYoYSkuZ29yKGEp
-fSwKRkw6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gSi5yWShhKS5kZChhLGIpfSwKR0E6ZnVuY3Rpb24oYSxi
-KXtyZXR1cm4gSi53MShhKS5FKGEsYil9LApIbTpmdW5jdGlvbihhKXtyZXR1cm4gSi5VNihhKS5nQShh
-KX0sCklUOmZ1bmN0aW9uKGEpe3JldHVybiBKLncxKGEpLmdtKGEpfSwKSnk6ZnVuY3Rpb24oYSxiKXty
-ZXR1cm4gSi5pYShhKS5lNyhhLGIpfSwKS1Y6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gSi5yWShhKS55bihh
-LGIpfSwKTHQ6ZnVuY3Rpb24oYSl7cmV0dXJuIEouWUUoYSkud2coYSl9LApNMTpmdW5jdGlvbihhLGIs
-Yyl7cmV0dXJuIEoudzEoYSkuRTIoYSxiLGMpfSwKTXU6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gSi5ZRShh
-KS5zUChhLGIpfSwKUXo6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gSi5yWShhKS5XKGEsYil9LApSTTpmdW5j
-dGlvbihhLGIpe2lmKGE9PW51bGwpcmV0dXJuIGI9PW51bGwKaWYodHlwZW9mIGEhPSJvYmplY3QiKXJl
-dHVybiBiIT1udWxsJiZhPT09YgpyZXR1cm4gSi5pYShhKS5ETihhLGIpfSwKUlg6ZnVuY3Rpb24oYSl7
-cmV0dXJuIEoudzEoYSkuYnIoYSl9LApUMDpmdW5jdGlvbihhKXtyZXR1cm4gSi5yWShhKS5iUyhhKX0s
-ClZ1OmZ1bmN0aW9uKGEpe3JldHVybiBKLnZkKGEpLnpRKGEpfSwKYTY6ZnVuY3Rpb24oYSxiKXtyZXR1
-cm4gSi5yWShhKS5PKGEsYil9LAphdTpmdW5jdGlvbihhLGIpe3JldHVybiBKLnJZKGEpLm5DKGEsYil9
-LApiVDpmdW5jdGlvbihhKXtyZXR1cm4gSi5ZRShhKS5ENChhKX0sCmNIOmZ1bmN0aW9uKGEpe3JldHVy
-biBKLnJZKGEpLmhjKGEpfSwKZFI6ZnVuY3Rpb24oYSl7cmV0dXJuIEouWUUoYSkuZ1AoYSl9LApkWjpm
-dW5jdGlvbihhLGIsYyxkKXtyZXR1cm4gSi5ZRShhKS5PbihhLGIsYyxkKX0sCmRnOmZ1bmN0aW9uKGEs
-YixjLGQpe3JldHVybiBKLnJZKGEpLmk3KGEsYixjLGQpfSwKZGg6ZnVuY3Rpb24oYSl7cmV0dXJuIEou
+PV8uUT1fLno9bnVsbH0sCkc6ZnVuY3Rpb24gRygpe3RoaXMuYz10aGlzLmI9dGhpcy5hPW51bGx9LApr
+UzpmdW5jdGlvbiBrUygpe30sCmlNOmZ1bmN0aW9uIGlNKGEpe3RoaXMuYT1hfSwKUjk6ZnVuY3Rpb24o
+YSl7cmV0dXJuIHQudy5iKGEpfHx0LkIuYihhKXx8dC5kei5iKGEpfHx0LkkuYihhKXx8dC5BLmIoYSl8
+fHQuZzQuYihhKXx8dC5nMi5iKGEpfSwKRlA6ZnVuY3Rpb24oYSl7cmV0dXJuIEgudihILmooYSkpfX0s
+Sj17ClF1OmZ1bmN0aW9uKGEsYixjLGQpe3JldHVybntpOmEscDpiLGU6Yyx4OmR9fSwKa3M6ZnVuY3Rp
+b24oYSl7dmFyIHMscixxLHAsbyxuPWFbdi5kaXNwYXRjaFByb3BlcnR5TmFtZV0KaWYobj09bnVsbClp
+ZigkLkJ2PT1udWxsKXtILlhEKCkKbj1hW3YuZGlzcGF0Y2hQcm9wZXJ0eU5hbWVdfWlmKG4hPW51bGwp
+e3M9bi5wCmlmKCExPT09cylyZXR1cm4gbi5pCmlmKCEwPT09cylyZXR1cm4gYQpyPU9iamVjdC5nZXRQ
+cm90b3R5cGVPZihhKQppZihzPT09cilyZXR1cm4gbi5pCmlmKG4uZT09PXIpdGhyb3cgSC5iKFAuU1ko
+IlJldHVybiBpbnRlcmNlcHRvciBmb3IgIitILkVqKHMoYSxuKSkpKX1xPWEuY29uc3RydWN0b3IKaWYo
+cT09bnVsbClwPW51bGwKZWxzZXtvPSQuem0KaWYobz09bnVsbClvPSQuem09di5nZXRJc29sYXRlVGFn
+KCJfJGRhcnRfanMiKQpwPXFbb119aWYocCE9bnVsbClyZXR1cm4gcApwPUgudzMoYSkKaWYocCE9bnVs
+bClyZXR1cm4gcAppZih0eXBlb2YgYT09ImZ1bmN0aW9uIilyZXR1cm4gQy5ERwpzPU9iamVjdC5nZXRQ
+cm90b3R5cGVPZihhKQppZihzPT1udWxsKXJldHVybiBDLlpRCmlmKHM9PT1PYmplY3QucHJvdG90eXBl
+KXJldHVybiBDLlpRCmlmKHR5cGVvZiBxPT0iZnVuY3Rpb24iKXtvPSQuem0KaWYobz09bnVsbClvPSQu
+em09di5nZXRJc29sYXRlVGFnKCJfJGRhcnRfanMiKQpPYmplY3QuZGVmaW5lUHJvcGVydHkocSxvLHt2
+YWx1ZTpDLnZCLGVudW1lcmFibGU6ZmFsc2Usd3JpdGFibGU6dHJ1ZSxjb25maWd1cmFibGU6dHJ1ZX0p
+CnJldHVybiBDLnZCfXJldHVybiBDLnZCfSwKUWk6ZnVuY3Rpb24oYSxiKXtpZihhPDB8fGE+NDI5NDk2
+NzI5NSl0aHJvdyBILmIoUC5URShhLDAsNDI5NDk2NzI5NSwibGVuZ3RoIixudWxsKSkKcmV0dXJuIEou
+cHkobmV3IEFycmF5KGEpLGIpfSwKS2g6ZnVuY3Rpb24oYSxiKXtpZihhPDApdGhyb3cgSC5iKFAueFko
+Ikxlbmd0aCBtdXN0IGJlIGEgbm9uLW5lZ2F0aXZlIGludGVnZXI6ICIrYSkpCnJldHVybiBILlFJKG5l
+dyBBcnJheShhKSxiLkMoImpkPDA+IikpfSwKcHk6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gSi5FcChILlFJ
+KGEsYi5DKCJqZDwwPiIpKSxiKX0sCkVwOmZ1bmN0aW9uKGEsYil7YS5maXhlZCRsZW5ndGg9QXJyYXkK
+cmV0dXJuIGF9LAp6QzpmdW5jdGlvbihhKXthLmZpeGVkJGxlbmd0aD1BcnJheQphLmltbXV0YWJsZSRs
+aXN0PUFycmF5CnJldHVybiBhfSwKR2E6ZnVuY3Rpb24oYSl7aWYoYTwyNTYpc3dpdGNoKGEpe2Nhc2Ug
+OTpjYXNlIDEwOmNhc2UgMTE6Y2FzZSAxMjpjYXNlIDEzOmNhc2UgMzI6Y2FzZSAxMzM6Y2FzZSAxNjA6
+cmV0dXJuITAKZGVmYXVsdDpyZXR1cm4hMX1zd2l0Y2goYSl7Y2FzZSA1NzYwOmNhc2UgODE5MjpjYXNl
+IDgxOTM6Y2FzZSA4MTk0OmNhc2UgODE5NTpjYXNlIDgxOTY6Y2FzZSA4MTk3OmNhc2UgODE5ODpjYXNl
+IDgxOTk6Y2FzZSA4MjAwOmNhc2UgODIwMTpjYXNlIDgyMDI6Y2FzZSA4MjMyOmNhc2UgODIzMzpjYXNl
+IDgyMzk6Y2FzZSA4Mjg3OmNhc2UgMTIyODg6Y2FzZSA2NTI3OTpyZXR1cm4hMApkZWZhdWx0OnJldHVy
+biExfX0sCm1tOmZ1bmN0aW9uKGEsYil7dmFyIHMscgpmb3Iocz1hLmxlbmd0aDtiPHM7KXtyPUMueEIu
+VyhhLGIpCmlmKHIhPT0zMiYmciE9PTEzJiYhSi5HYShyKSlicmVhazsrK2J9cmV0dXJuIGJ9LApjMTpm
+dW5jdGlvbihhLGIpe3ZhciBzLHIKZm9yKDtiPjA7Yj1zKXtzPWItMQpyPUMueEIuTyhhLHMpCmlmKHIh
+PT0zMiYmciE9PTEzJiYhSi5HYShyKSlicmVha31yZXR1cm4gYn0sClU2OmZ1bmN0aW9uKGEpe2lmKHR5
+cGVvZiBhPT0ic3RyaW5nIilyZXR1cm4gSi5Eci5wcm90b3R5cGUKaWYoYT09bnVsbClyZXR1cm4gYQpp
+ZihhLmNvbnN0cnVjdG9yPT1BcnJheSlyZXR1cm4gSi5qZC5wcm90b3R5cGUKaWYodHlwZW9mIGEhPSJv
+YmplY3QiKXtpZih0eXBlb2YgYT09ImZ1bmN0aW9uIilyZXR1cm4gSi5jNS5wcm90b3R5cGUKcmV0dXJu
+IGF9aWYoYSBpbnN0YW5jZW9mIFAuTWgpcmV0dXJuIGEKcmV0dXJuIEoua3MoYSl9LApZRTpmdW5jdGlv
+bihhKXtpZihhPT1udWxsKXJldHVybiBhCmlmKHR5cGVvZiBhIT0ib2JqZWN0Iil7aWYodHlwZW9mIGE9
+PSJmdW5jdGlvbiIpcmV0dXJuIEouYzUucHJvdG90eXBlCnJldHVybiBhfWlmKGEgaW5zdGFuY2VvZiBQ
+Lk1oKXJldHVybiBhCnJldHVybiBKLmtzKGEpfSwKaWE6ZnVuY3Rpb24oYSl7aWYodHlwZW9mIGE9PSJu
+dW1iZXIiKXtpZihNYXRoLmZsb29yKGEpPT1hKXJldHVybiBKLmJVLnByb3RvdHlwZQpyZXR1cm4gSi5r
+RC5wcm90b3R5cGV9aWYodHlwZW9mIGE9PSJzdHJpbmciKXJldHVybiBKLkRyLnByb3RvdHlwZQppZihh
+PT1udWxsKXJldHVybiBKLndlLnByb3RvdHlwZQppZih0eXBlb2YgYT09ImJvb2xlYW4iKXJldHVybiBK
+LnlFLnByb3RvdHlwZQppZihhLmNvbnN0cnVjdG9yPT1BcnJheSlyZXR1cm4gSi5qZC5wcm90b3R5cGUK
+aWYodHlwZW9mIGEhPSJvYmplY3QiKXtpZih0eXBlb2YgYT09ImZ1bmN0aW9uIilyZXR1cm4gSi5jNS5w
+cm90b3R5cGUKcmV0dXJuIGF9aWYoYSBpbnN0YW5jZW9mIFAuTWgpcmV0dXJuIGEKcmV0dXJuIEoua3Mo
+YSl9LApyWTpmdW5jdGlvbihhKXtpZih0eXBlb2YgYT09InN0cmluZyIpcmV0dXJuIEouRHIucHJvdG90
+eXBlCmlmKGE9PW51bGwpcmV0dXJuIGEKaWYoIShhIGluc3RhbmNlb2YgUC5NaCkpcmV0dXJuIEoua2Qu
+cHJvdG90eXBlCnJldHVybiBhfSwKdzE6ZnVuY3Rpb24oYSl7aWYoYT09bnVsbClyZXR1cm4gYQppZihh
+LmNvbnN0cnVjdG9yPT1BcnJheSlyZXR1cm4gSi5qZC5wcm90b3R5cGUKaWYodHlwZW9mIGEhPSJvYmpl
+Y3QiKXtpZih0eXBlb2YgYT09ImZ1bmN0aW9uIilyZXR1cm4gSi5jNS5wcm90b3R5cGUKcmV0dXJuIGF9
+aWYoYSBpbnN0YW5jZW9mIFAuTWgpcmV0dXJuIGEKcmV0dXJuIEoua3MoYSl9LApBNTpmdW5jdGlvbihh
+LGIpe3JldHVybiBKLncxKGEpLmVSKGEsYil9LApFaDpmdW5jdGlvbihhLGIsYyl7cmV0dXJuIEouWUUo
+YSkubUsoYSxiLGMpfSwKRWw6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gSi53MShhKS5kcihhLGIpfSwKRjc6
+ZnVuY3Rpb24oYSl7cmV0dXJuIEouVTYoYSkuZ29yKGEpfSwKRkw6ZnVuY3Rpb24oYSxiKXtyZXR1cm4g
+Si5yWShhKS5kZChhLGIpfSwKR0E6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gSi53MShhKS5FKGEsYil9LApI
+bTpmdW5jdGlvbihhKXtyZXR1cm4gSi5VNihhKS5nQShhKX0sCklUOmZ1bmN0aW9uKGEpe3JldHVybiBK
+LncxKGEpLmdtKGEpfSwKSnk6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gSi5pYShhKS5lNyhhLGIpfSwKTHQ6
+ZnVuY3Rpb24oYSl7cmV0dXJuIEouWUUoYSkud2coYSl9LApNMTpmdW5jdGlvbihhLGIsYyl7cmV0dXJu
+IEoudzEoYSkuRTIoYSxiLGMpfSwKTXU6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gSi5ZRShhKS5zUChhLGIp
+fSwKUk06ZnVuY3Rpb24oYSxiKXtpZihhPT1udWxsKXJldHVybiBiPT1udWxsCmlmKHR5cGVvZiBhIT0i
+b2JqZWN0IilyZXR1cm4gYiE9bnVsbCYmYT09PWIKcmV0dXJuIEouaWEoYSkuRE4oYSxiKX0sClJYOmZ1
+bmN0aW9uKGEpe3JldHVybiBKLncxKGEpLmJyKGEpfSwKVDA6ZnVuY3Rpb24oYSl7cmV0dXJuIEouclko
+YSkuYlMoYSl9LAphNjpmdW5jdGlvbihhLGIpe3JldHVybiBKLnJZKGEpLk8oYSxiKX0sCmJUOmZ1bmN0
+aW9uKGEpe3JldHVybiBKLllFKGEpLkQ0KGEpfSwKY0g6ZnVuY3Rpb24oYSl7cmV0dXJuIEouclkoYSku
+aGMoYSl9LApkUjpmdW5jdGlvbihhKXtyZXR1cm4gSi5ZRShhKS5nUChhKX0sCmRaOmZ1bmN0aW9uKGEs
+YixjLGQpe3JldHVybiBKLllFKGEpLk9uKGEsYixjLGQpfSwKZGg6ZnVuY3Rpb24oYSl7cmV0dXJuIEou
WUUoYSkuRkYoYSl9LApkcjpmdW5jdGlvbihhLGIpe3JldHVybiBKLllFKGEpLnNhNChhLGIpfSwKaGY6
ZnVuY3Rpb24oYSl7cmV0dXJuIEouaWEoYSkuZ2lPKGEpfSwKaWc6ZnVuY3Rpb24oYSl7cmV0dXJuIEou
-WUUoYSkuZ1FnKGEpfSwKbDU6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gSi5ZRShhKS5zaGYoYSxiKX0sCmxk
-OmZ1bmN0aW9uKGEsYixjKXtyZXR1cm4gSi5yWShhKS5OaihhLGIsYyl9LApwNDpmdW5jdGlvbihhLGIp
-e3JldHVybiBKLnJZKGEpLlRjKGEsYil9LApwYjpmdW5jdGlvbihhLGIpe2lmKHR5cGVvZiBhPT0ibnVt
-YmVyIiYmdHlwZW9mIGI9PSJudW1iZXIiKXJldHVybiBhK2IKcmV0dXJuIEouUWMoYSkuaChhLGIpfSwK
-cTA6ZnVuY3Rpb24oYSxiLGMpe3JldHVybiBKLnJZKGEpLlFpKGEsYixjKX0sCnFGOmZ1bmN0aW9uKGEp
-e3JldHVybiBKLllFKGEpLmdWbChhKX0sCnRIOmZ1bmN0aW9uKGEsYixjKXtyZXR1cm4gSi5ZRShhKS5w
-ayhhLGIsYyl9LAp1OTpmdW5jdGlvbihhLGIsYyl7cmV0dXJuIEoudzEoYSkuWTUoYSxiLGMpfSwKdVU6
-ZnVuY3Rpb24oYSl7cmV0dXJuIEouVTYoYSkuZ2wwKGEpfSwKdzpmdW5jdGlvbihhKXtyZXR1cm4gSi5p
-YShhKS53KGEpfSwKd2Y6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gSi5ZRShhKS5zUk4oYSxiKX0sCng5OmZ1
-bmN0aW9uKGEsYil7aWYodHlwZW9mIGI9PT0ibnVtYmVyIilpZihhLmNvbnN0cnVjdG9yPT1BcnJheXx8
-dHlwZW9mIGE9PSJzdHJpbmcifHxILndWKGEsYVt2LmRpc3BhdGNoUHJvcGVydHlOYW1lXSkpaWYoYj4+
-PjA9PT1iJiZiPGEubGVuZ3RoKXJldHVybiBhW2JdCnJldHVybiBKLlU2KGEpLnEoYSxiKX0sCnpsOmZ1
-bmN0aW9uKGEsYil7cmV0dXJuIEouVTYoYSkudGcoYSxiKX0sCkd2OmZ1bmN0aW9uIEd2KCl7fSwKeUU6
-ZnVuY3Rpb24geUUoKXt9LAp3ZTpmdW5jdGlvbiB3ZSgpe30sCk1GOmZ1bmN0aW9uIE1GKCl7fSwKaUM6
-ZnVuY3Rpb24gaUMoKXt9LAprZDpmdW5jdGlvbiBrZCgpe30sCmM1OmZ1bmN0aW9uIGM1KCl7fSwKamQ6
-ZnVuY3Rpb24gamQoYSl7dGhpcy4kdGk9YX0sClBvOmZ1bmN0aW9uIFBvKGEpe3RoaXMuJHRpPWF9LApt
-MTpmdW5jdGlvbiBtMShhLGIsYyl7dmFyIF89dGhpcwpfLmE9YQpfLmI9YgpfLmM9MApfLmQ9bnVsbApf
-LiR0aT1jfSwKcUk6ZnVuY3Rpb24gcUkoKXt9LApiVTpmdW5jdGlvbiBiVSgpe30sCmtEOmZ1bmN0aW9u
-IGtEKCl7fSwKRHI6ZnVuY3Rpb24gRHIoKXt9fSxMPXsKSXE6ZnVuY3Rpb24oKXtDLkJaLkIoZG9jdW1l
-bnQsIkRPTUNvbnRlbnRMb2FkZWQiLG5ldyBMLmUoKSkKQy5vbC5CKHdpbmRvdywicG9wc3RhdGUiLG5l
-dyBMLkwoKSl9LAprejpmdW5jdGlvbihhKXt2YXIgcyxyPXQuZy5hKGEucGFyZW50Tm9kZSkucXVlcnlT
-ZWxlY3RvcigiOnNjb3BlID4gdWwiKSxxPXIuc3R5bGUscD0iIitDLkNELnpRKHIub2Zmc2V0SGVpZ2h0
-KSoyKyJweCIKcS5tYXhIZWlnaHQ9cApxPUoucUYoYSkKcD1xLiR0aQpzPXAuQygifigxKT8iKS5hKG5l
-dyBMLld4KHIsYSkpCnQuWi5hKG51bGwpClcuSkUocS5hLHEuYixzLCExLHAuYyl9LAp5WDpmdW5jdGlv
-bihhLGIpe3ZhciBzLHIscSxwLG8sbixtPSJxdWVyeVNlbGVjdG9yQWxsIixsPWRvY3VtZW50LnF1ZXJ5
-U2VsZWN0b3IoYSksaz10LmcKbC50b1N0cmluZwpzPXQuaApILkRoKGsscywiVCIsbSkKcj10LlIKcT1u
-ZXcgVy53eihsLnF1ZXJ5U2VsZWN0b3JBbGwoIi5uYXYtbGluayIpLHIpCnEuSyhxLG5ldyBMLkFPKGIp
-KQpILkRoKGsscywiVCIsbSkKcD1uZXcgVy53eihsLnF1ZXJ5U2VsZWN0b3JBbGwoIi5yZWdpb24iKSxy
-KQppZighcC5nbDAocCkpe289bC5xdWVyeVNlbGVjdG9yKCJ0YWJsZVtkYXRhLXBhdGhdIikKby50b1N0
-cmluZwpwLksocCxuZXcgTC5IbyhvLmdldEF0dHJpYnV0ZSgiZGF0YS0iK25ldyBXLlN5KG5ldyBXLmk3
-KG8pKS5PVSgicGF0aCIpKSkpfUguRGgoayxzLCJUIixtKQpuPW5ldyBXLnd6KGwucXVlcnlTZWxlY3Rv
-ckFsbCgiLmFkZC1oaW50LWxpbmsiKSxyKQpuLksobixuZXcgTC5JQygpKX0sClE2OmZ1bmN0aW9uKGEs
-YixjKXt2YXIgcz1uZXcgWE1MSHR0cFJlcXVlc3QoKQpDLkR0LmVvKHMsIkdFVCIsTC5RNChhLGIpLCEw
-KQpzLnNldFJlcXVlc3RIZWFkZXIoIkNvbnRlbnQtVHlwZSIsImFwcGxpY2F0aW9uL2pzb247IGNoYXJz
-ZXQ9VVRGLTgiKQpyZXR1cm4gTC5MVShzLG51bGwsYy5DKCIwKiIpKX0sCnR5OmZ1bmN0aW9uKGEsYil7
-dmFyIHM9bmV3IFhNTEh0dHBSZXF1ZXN0KCkscj10LlgKQy5EdC5lbyhzLCJQT1NUIixMLlE0KGEsUC5G
-bChyLHIpKSwhMCkKcy5zZXRSZXF1ZXN0SGVhZGVyKCJDb250ZW50LVR5cGUiLCJhcHBsaWNhdGlvbi9q
-c29uOyBjaGFyc2V0PVVURi04IikKcmV0dXJuIEwuTFUocyxiLHQudCl9LApMVTpmdW5jdGlvbihhLGIs
-Yyl7cmV0dXJuIEwuVGcoYSxiLGMsYy5DKCIwKiIpKX0sClRnOmZ1bmN0aW9uKGEsYixjLGQpe3ZhciBz
-PTAscj1QLkZYKGQpLHEscD0yLG8sbj1bXSxtLGwsayxqLGksaCxnLGYsZQp2YXIgJGFzeW5jJExVPVAu
-bHooZnVuY3Rpb24oYTAsYTEpe2lmKGEwPT09MSl7bz1hMQpzPXB9d2hpbGUodHJ1ZSlzd2l0Y2gocyl7
-Y2FzZSAwOmg9bmV3IFAuWmYobmV3IFAudnMoJC5YMyx0LmdWKSx0LmJDKQpnPXQuZWIKZj1nLmEobmV3
-IEwuZkMoaCxhKSkKdC5aLmEobnVsbCkKaz10LmVRClcuSkUoYSwibG9hZCIsZiwhMSxrKQpXLkpFKGEs
-ImVycm9yIixnLmEoaC5nWUooKSksITEsaykKYS5zZW5kKGI9PW51bGw/bnVsbDpDLkN0Lk9CKGIsbnVs
-bCkpCnA9NApzPTcKcmV0dXJuIFAualEoaC5hLCRhc3luYyRMVSkKY2FzZSA3OnA9MgpzPTYKYnJlYWsK
-Y2FzZSA0OnA9MwplPW8KSC5SdShlKQptPUgudHMoZSkKZz1hLnJlYWR5U3RhdGUKaWYoZz09PTQmJmEu
-c3RhdHVzPT09MCl0aHJvdyBILmIoTC5URygiRXJyb3IgcmVhY2hpbmcgbWlncmF0aW9uIHByZXZpZXcg
-c2VydmVyIiwiVGhpcyB1c3VhbGx5IGhhcHBlbnMgYmVjYXVzZSB0aGUgbWlncmF0aW9uIHByZXZpZXcg
-c2VydmVyIGhhcyBleGl0ZWQuICBGb3JcbmV4YW1wbGUgaXQgbWF5IGhhdmUgYmVlbiBhYm9ydGVkIHdp
-dGggQ3RybC1DLCBvciB5b3UgbWF5IGhhdmUgY29tcGxldGVkIHRoaXNcbm1pZ3JhdGlvbiwgb3IgYW4g
-ZXhjZXB0aW9uIG1heSBoYXZlIG9jY3VycmVkLiAgUGxlYXNlIGNoZWNrIHRoZSBjb25zb2xlIHdoZXJl
-XG55b3UgaW52b2tlZCBgZGFydCBtaWdyYXRlYCB0byB2ZXJpZnkgdGhhdCB0aGUgcHJldmlldyBzZXJ2
-ZXIgaXMgc3RpbGwgcnVubmluZy5cbiIpKQplbHNle2w9bmV3IEgubEooSC5RSShbInJlYWR5U3RhdGU9
-IitILkVqKGcpLCJyZXNwb25zZVRleHQ9IitDLkN0Lk9CKGEucmVzcG9uc2VUZXh0LG51bGwpLCJyZXNw
-b25zZVR5cGU9IitDLkN0Lk9CKGEucmVzcG9uc2VUeXBlLG51bGwpLCJyZXNwb25zZVVybD0iK0MuQ3Qu
-T0IoYS5yZXNwb25zZVVSTCxudWxsKSwic3RhdHVzPSIrSC5FaihhLnN0YXR1cyksInN0YXR1c1RleHQ9
-IitDLkN0Lk9CKGEuc3RhdHVzVGV4dCxudWxsKV0sdC5pKSx0LmVxLmEobmV3IEwuVG0oKSksdC5mUCku
-aygwLCIsICIpCnRocm93IEguYihQLlRsKCJFcnJvciByZWFjaGluZyBtaWdyYXRpb24gcHJldmlldyBz
-ZXJ2ZXI6ICIrSC5FaihsKSxtKSl9cz02CmJyZWFrCmNhc2UgMzpzPTIKYnJlYWsKY2FzZSA2OmlmKGEu
-c3RhdHVzPT09NDAxKXRocm93IEguYihMLlRHKCJVbmF1dGhvcml6ZWQgcmVzcG9uc2UgZnJvbSBtaWdy
-YXRpb24gcHJldmlldyBzZXJ2ZXIiLCJUaGUgbWlncmF0aW9uIHByZXZpZXcgc2VydmVyIGhhcyBkZXRl
-Y3RlZCBhIG1pc21hdGNoIGJldHdlZW4gdGhlIGF1dGhUb2tlbiBpblxueW91ciBVUkwgYW5kIHRoZSB0
-b2tlbiB0aGF0IHdhcyBnZW5lcmF0ZWQgYXQgdGhlIHRpbWUgdGhhdCBgZGFydCBtaWdyYXRlYCB3YXNc
-bnJ1bi4gIEhhdmUgeW91IHJlc3RhcnRlZCB0aGUgbWlncmF0aW9uIHNlcnZlciByZWNlbnRseT8gIElm
-IHNvLCB5b3UnbGwgbmVlZCB0b1xuY2hlY2sgaXRzIG91dHB1dCBmb3IgYSBmcmVzaCBVUkwsIGFuZCB1
-c2UgdGhhdCBVUkwgdG8gcGVyZm9ybSB5b3VyIG1pZ3JhdGlvbi5cbiIpKQppPUMuQ3QucFcoMCxhLnJl
-c3BvbnNlVGV4dCxudWxsKQppZihhLnN0YXR1cz09PTIwMCl7cT1jLkMoIjAqIikuYShpKQpzPTEKYnJl
-YWt9ZWxzZSB0aHJvdyBILmIoaSkKY2FzZSAxOnJldHVybiBQLnlDKHEscikKY2FzZSAyOnJldHVybiBQ
-LmYzKG8scil9fSkKcmV0dXJuIFAuREkoJGFzeW5jJExVLHIpfSwKYUs6ZnVuY3Rpb24oYSl7dmFyIHM9
-UC5oSyhhKS5naFkoKS5xKDAsImxpbmUiKQpyZXR1cm4gcz09bnVsbD9udWxsOkguSHAocyxudWxsKX0s
-Ckc2OmZ1bmN0aW9uKGEpe3ZhciBzPVAuaEsoYSkuZ2hZKCkucSgwLCJvZmZzZXQiKQpyZXR1cm4gcz09
-bnVsbD9udWxsOkguSHAocyxudWxsKX0sCmk2OmZ1bmN0aW9uKGEpe3JldHVybiBMLm5XKHQuTy5hKGEp
-KX0sCm5XOmZ1bmN0aW9uKGEpe3ZhciBzPTAscj1QLkZYKHQueikscT0xLHAsbz1bXSxuLG0sbCxrLGos
-aSxoCnZhciAkYXN5bmMkaTY9UC5seihmdW5jdGlvbihiLGMpe2lmKGI9PT0xKXtwPWMKcz1xfXdoaWxl
-KHRydWUpc3dpdGNoKHMpe2Nhc2UgMDppPXQuZy5hKFcucWMoYS5jdXJyZW50VGFyZ2V0KSkuZ2V0QXR0
-cmlidXRlKCJocmVmIikKYS5wcmV2ZW50RGVmYXVsdCgpCnE9MwprPWRvY3VtZW50Cm49Qy5DRC56UShr
-LnF1ZXJ5U2VsZWN0b3IoIi5jb250ZW50Iikuc2Nyb2xsVG9wKQpzPTYKcmV0dXJuIFAualEoTC50eShp
-LG51bGwpLCRhc3luYyRpNikKY2FzZSA2OnM9NwpyZXR1cm4gUC5qUShMLkc3KHdpbmRvdy5sb2NhdGlv
-bi5wYXRobmFtZSxudWxsLG51bGwsITEsbnVsbCksJGFzeW5jJGk2KQpjYXNlIDc6ay5ib2R5LmNsYXNz
-TGlzdC5hZGQoIm5lZWRzLXJlcnVuIikKaz1rLnF1ZXJ5U2VsZWN0b3IoIi5jb250ZW50IikKay50b1N0
-cmluZwprLnNjcm9sbFRvcD1KLlZ1KG4pCnE9MQpzPTUKYnJlYWsKY2FzZSAzOnE9MgpoPXAKbT1ILlJ1
-KGgpCmw9SC50cyhoKQpMLkMyKCJjb3VsZG4ndCBhZGQvcmVtb3ZlIGhpbnQiLG0sbCkKcz01CmJyZWFr
-CmNhc2UgMjpzPTEKYnJlYWsKY2FzZSA1OnJldHVybiBQLnlDKG51bGwscikKY2FzZSAxOnJldHVybiBQ
-LmYzKHAscil9fSkKcmV0dXJuIFAuREkoJGFzeW5jJGk2LHIpfSwKQzI6ZnVuY3Rpb24oYSxiLGMpe3Zh
-ciBzLHIscSxwLG8sbj0iZXhjZXB0aW9uIixtPSJzdGFja1RyYWNlIgppZih0LnQuYihiKSYmSi5STShi
-LnEoMCwic3VjY2VzcyIpLCExKSYmYi54NChuKSYmYi54NChtKSl7cz1KLlU2KGIpCnI9SC5oKHMucShi
-LG4pKQpjPXMucShiLG0pCnE9bnVsbH1lbHNlIGlmKGIgaW5zdGFuY2VvZiBMLlFXKXtyPWIuYQpxPWIu
-Yn1lbHNle3I9Si53KGIpCnE9bnVsbH1pZihxPT1udWxsKXE9YwpzPWRvY3VtZW50CnA9cy5xdWVyeVNl
-bGVjdG9yKCIucG9wdXAtcGFuZSIpCnAucXVlcnlTZWxlY3RvcigiaDIiKS5pbm5lclRleHQ9YQpwLnF1
-ZXJ5U2VsZWN0b3IoInAiKS5pbm5lclRleHQ9cgpwLnF1ZXJ5U2VsZWN0b3IoInByZSIpLmlubmVyVGV4
-dD1KLncocSkKbz10LmRkLmEocC5xdWVyeVNlbGVjdG9yKCJhLmJvdHRvbSIpKTsobyYmQy54bikuc0xV
-KG8sUC5YZCgiaHR0cHMiLCJnaXRodWIuY29tIiwiZGFydC1sYW5nL3Nkay9pc3N1ZXMvbmV3IixQLkVG
-KFsidGl0bGUiLCJDdXN0b21lci1yZXBvcnRlZCBpc3N1ZSB3aXRoIE5OQkQgbWlncmF0aW9uIHRvb2w6
-ICIrYSwibGFiZWxzIix1LmQsImJvZHkiLGErIlxuXG5FcnJvcjogIitILkVqKHIpKyJcblxuUGxlYXNl
-IGZpbGwgaW4gdGhlIGZvbGxvd2luZzpcblxuKipOYW1lIG9mIHBhY2thZ2UgYmVpbmcgbWlncmF0ZWQg
-KGlmIHB1YmxpYykqKjpcbioqV2hhdCBJIHdhcyBkb2luZyB3aGVuIHRoaXMgaXNzdWUgb2NjdXJyZWQq
-KjpcbioqSXMgaXQgcG9zc2libGUgdG8gd29yayBhcm91bmQgdGhpcyBpc3N1ZSoqOlxuKipIYXMgdGhp
-cyBpc3N1ZSBoYXBwZW5lZCBiZWZvcmUsIGFuZCBpZiBzbywgaG93IG9mdGVuKio6XG4qKkRhcnQgU0RL
-IHZlcnNpb24qKjogIitILkVqKHMuZ2V0RWxlbWVudEJ5SWQoInNkay12ZXJzaW9uIikudGV4dENvbnRl
-bnQpKyJcbioqQWRkaXRpb25hbCBkZXRhaWxzKio6XG5cblRoYW5rcyBmb3IgZmlsaW5nIVxuXG5TdGFj
-a3RyYWNlOiBfYXV0byBwb3B1bGF0ZWQgYnkgbWlncmF0aW9uIHByZXZpZXcgdG9vbC5fXG5cbmBgYFxu
-IitILkVqKGMpKyJcbmBgYFxuIl0sdC5YLHQueikpLmduRCgpKQpzPW8uc3R5bGUKcy5kaXNwbGF5PSJp
-bml0aWFsIgpzPXAuc3R5bGUKcy5kaXNwbGF5PSJpbml0aWFsIgpzPWErIjogIitILkVqKGIpCndpbmRv
-dwppZih0eXBlb2YgY29uc29sZSE9InVuZGVmaW5lZCIpd2luZG93LmNvbnNvbGUuZXJyb3IocykKd2lu
-ZG93CnM9SC5FaihjKQppZih0eXBlb2YgY29uc29sZSE9InVuZGVmaW5lZCIpd2luZG93LmNvbnNvbGUu
-ZXJyb3Iocyl9LAp0MjpmdW5jdGlvbihhLGIpe3ZhciBzLHIscSxwLG89dC5nLmEoVy5xYyhhLmN1cnJl
-bnRUYXJnZXQpKQphLnByZXZlbnREZWZhdWx0KCkKcz1vLmdldEF0dHJpYnV0ZSgiaHJlZiIpCnI9TC5V
-cyhzKQpxPUwuRzYocykKcD1MLmFLKHMpCmlmKHEhPW51bGwpTC5hZihyLHEscCxiLG5ldyBMLm5UKHIs
-cSxwKSkKZWxzZSBMLmFmKHIsbnVsbCxudWxsLGIsbmV3IEwuTlkocikpfSwKSzA6ZnVuY3Rpb24oYSl7
-dmFyIHMscixxLHA9ZG9jdW1lbnQucXVlcnlTZWxlY3RvcigiLnBvcHVwLXBhbmUiKQpwLnF1ZXJ5U2Vs
-ZWN0b3IoImgyIikuaW5uZXJUZXh0PSJGYWlsZWQgdG8gcmVydW4gZnJvbSBzb3VyY2VzIgpwLnF1ZXJ5
-U2VsZWN0b3IoInAiKS5pbm5lclRleHQ9IlNvdXJjZXMgY29udGFpbiBzdGF0aWMgYW5hbHlzaXMgZXJy
-b3JzOiIKcz1wLnF1ZXJ5U2VsZWN0b3IoInByZSIpCnI9Si5FbChhLHQuYXcpCnE9SC5MaChyKQpzLmlu
-bmVyVGV4dD1uZXcgSC5sSihyLHEuQygicVUqKGxELkUpIikuYShuZXcgTC51ZSgpKSxxLkMoImxKPGxE
-LkUscVUqPiIpKS5rKDAsIlxuIikKcT1wLnF1ZXJ5U2VsZWN0b3IoImEuYm90dG9tIikuc3R5bGUKcS5k
-aXNwbGF5PSJub25lIgpzPXAuc3R5bGUKcy5kaXNwbGF5PSJpbml0aWFsIn0sCnZVOmZ1bmN0aW9uKCl7
-dmFyIHM9ZG9jdW1lbnQKSC5EaCh0LmcsdC5oLCJUIiwicXVlcnlTZWxlY3RvckFsbCIpCnM9bmV3IFcu
-d3oocy5xdWVyeVNlbGVjdG9yQWxsKCIuY29kZSIpLHQuUikKcy5LKHMsbmV3IEwuZVgoKSl9LApoWDpm
-dW5jdGlvbihhLGIsYyl7cmV0dXJuIEwuWXcoYSxiLGMpfSwKWXc6ZnVuY3Rpb24oYSxiLGMpe3ZhciBz
-PTAscj1QLkZYKHQueikscT0xLHAsbz1bXSxuLG0sbCxrLGosaSxoLGcKdmFyICRhc3luYyRoWD1QLmx6
-KGZ1bmN0aW9uKGQsZSl7aWYoZD09PTEpe3A9ZQpzPXF9d2hpbGUodHJ1ZSlzd2l0Y2gocyl7Y2FzZSAw
-OnE9MwpqPXQuWApzPTYKcmV0dXJuIFAualEoTC5RNihhLFAuRUYoWyJyZWdpb24iLCJyZWdpb24iLCJv
-ZmZzZXQiLEguRWooYildLGosaiksdC50KSwkYXN5bmMkaFgpCmNhc2UgNjpuPWUKaj1uCmk9Si5VNihq
-KQptPW5ldyBVLmQyKFUuamYoaS5xKGosImVkaXRzIikpLEguaChpLnEoaiwiZXhwbGFuYXRpb24iKSks
-SC51UChpLnEoaiwibGluZSIpKSxILmgoaS5xKGosImRpc3BsYXlQYXRoIikpLEguaChpLnEoaiwidXJp
-UGF0aCIpKSxVLk5kKGkucShqLCJ0cmFjZXMiKSkpCkwuVDEobSkKTC5GcihhLGIsYykKTC55WCgiLmVk
-aXQtcGFuZWwgLnBhbmVsLWNvbnRlbnQiLCExKQpxPTEKcz01CmJyZWFrCmNhc2UgMzpxPTIKZz1wCmw9
-SC5SdShnKQprPUgudHMoZykKTC5DMigiY291bGRuJ3QgbG9hZCBlZGl0IGRldGFpbHMiLGwsaykKcz01
-CmJyZWFrCmNhc2UgMjpzPTEKYnJlYWsKY2FzZSA1OnJldHVybiBQLnlDKG51bGwscikKY2FzZSAxOnJl
-dHVybiBQLmYzKHAscil9fSkKcmV0dXJuIFAuREkoJGFzeW5jJGhYLHIpfSwKRzc6ZnVuY3Rpb24oYSxi
-LGMsZCxlKXtyZXR1cm4gTC5MNShhLGIsYyxkLGUpfSwKTDU6ZnVuY3Rpb24oYSxiLGMsZCxlKXt2YXIg
-cz0wLHI9UC5GWCh0LkgpLHEscD0yLG8sbj1bXSxtLGwsayxqLGksaCxnCnZhciAkYXN5bmMkRzc9UC5s
-eihmdW5jdGlvbihmLGEwKXtpZihmPT09MSl7bz1hMApzPXB9d2hpbGUodHJ1ZSlzd2l0Y2gocyl7Y2Fz
-ZSAwOmlmKCFKLnA0KGEsIi5kYXJ0Iikpe0wuQkUoYSxCLndSKCksZCkKTC5CWChhLG51bGwpCmlmKGUh
-PW51bGwpZS4kMCgpCnM9MQpicmVha31wPTQKaT10LlgKcz03CnJldHVybiBQLmpRKEwuUTYoYSxQLkVG
-KFsiaW5saW5lIiwidHJ1ZSJdLGksaSksdC50KSwkYXN5bmMkRzcpCmNhc2UgNzptPWEwCkwuQkUoYSxC
-LllmKG0pLGQpCkwuZkcoYixjKQpsPUwuVXMoYSkKTC5CWChsLGIpCmlmKGUhPW51bGwpZS4kMCgpCnA9
-MgpzPTYKYnJlYWsKY2FzZSA0OnA9MwpnPW8Kaz1ILlJ1KGcpCmo9SC50cyhnKQpMLkMyKCJjb3VsZG4n
-dCBsb2FkIGRhcnQgZmlsZSAiK2EsayxqKQpzPTYKYnJlYWsKY2FzZSAzOnM9MgpicmVhawpjYXNlIDY6
-Y2FzZSAxOnJldHVybiBQLnlDKHEscikKY2FzZSAyOnJldHVybiBQLmYzKG8scil9fSkKcmV0dXJuIFAu
-REkoJGFzeW5jJEc3LHIpfSwKR2U6ZnVuY3Rpb24oKXt2YXIgcz0wLHI9UC5GWCh0LnopLHE9MSxwLG89
-W10sbixtLGwsayxqLGksaCxnCnZhciAkYXN5bmMkR2U9UC5seihmdW5jdGlvbihhLGIpe2lmKGE9PT0x
-KXtwPWIKcz1xfXdoaWxlKHRydWUpc3dpdGNoKHMpe2Nhc2UgMDpoPSIvX3ByZXZpZXcvbmF2aWdhdGlv
-blRyZWUuanNvbiIKcT0zCnM9NgpyZXR1cm4gUC5qUShMLlE2KGgsQy5DTSx0LmVFKSwkYXN5bmMkR2Up
-CmNhc2UgNjpuPWIKbT1kb2N1bWVudC5xdWVyeVNlbGVjdG9yKCIubmF2LXRyZWUiKQpKLmw1KG0sIiIp
-Cmo9TC5tSyhuKQokLklSPWoKTC50WChtLGosITApCnE9MQpzPTUKYnJlYWsKY2FzZSAzOnE9MgpnPXAK
-bD1ILlJ1KGcpCms9SC50cyhnKQpMLkMyKCJjb3VsZG4ndCBsb2FkIG5hdmlnYXRpb24gdHJlZSIsbCxr
-KQpzPTUKYnJlYWsKY2FzZSAyOnM9MQpicmVhawpjYXNlIDU6cmV0dXJuIFAueUMobnVsbCxyKQpjYXNl
-IDE6cmV0dXJuIFAuZjMocCxyKX19KQpyZXR1cm4gUC5ESSgkYXN5bmMkR2Uscil9LApxTzpmdW5jdGlv
-bihhKXt2YXIgcyxyPWEuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkscT1DLkNELnpRKCQuZmkoKS5vZmZz
-ZXRIZWlnaHQpLHA9d2luZG93LmlubmVySGVpZ2h0LG89Qy5DRC56USgkLkRXKCkub2Zmc2V0SGVpZ2h0
-KQppZih0eXBlb2YgcCE9PSJudW1iZXIiKXJldHVybiBwLkhOKCkKcz1yLmJvdHRvbQpzLnRvU3RyaW5n
-CmlmKHM+cC0obysxNCkpSi5kaChhKQplbHNle3A9ci50b3AKcC50b1N0cmluZwppZihwPHErMTQpSi5k
-aChhKX19LApmRzpmdW5jdGlvbihhLGIpe3ZhciBzLHIscSxwLG8KaWYoYSE9bnVsbCl7cz1kb2N1bWVu
-dApyPXMuZ2V0RWxlbWVudEJ5SWQoIm8iK0guRWooYSkpCnE9cy5xdWVyeVNlbGVjdG9yKCIubGluZS0i
-K0guRWooYikpCmlmKHIhPW51bGwpe0wucU8ocikKSi5kUihyKS5pKDAsInRhcmdldCIpfWVsc2UgaWYo
-cSE9bnVsbClMLnFPKHEucGFyZW50RWxlbWVudCkKaWYocSE9bnVsbClKLmRSKHQuZy5hKHEucGFyZW50
-Tm9kZSkpLmkoMCwiaGlnaGxpZ2h0Iil9ZWxzZXtzPWRvY3VtZW50CnA9dC5nCkguRGgocCx0LmgsIlQi
+WUUoYSkuZ1FnKGEpfSwKbDU6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gSi5ZRShhKS5zaGYoYSxiKX0sCnFG
+OmZ1bmN0aW9uKGEpe3JldHVybiBKLllFKGEpLmdWbChhKX0sCnRIOmZ1bmN0aW9uKGEsYixjKXtyZXR1
+cm4gSi5ZRShhKS5wayhhLGIsYyl9LAp1OTpmdW5jdGlvbihhLGIsYyl7cmV0dXJuIEoudzEoYSkuWTUo
+YSxiLGMpfSwKdVU6ZnVuY3Rpb24oYSl7cmV0dXJuIEouVTYoYSkuZ2wwKGEpfSwKdzpmdW5jdGlvbihh
+KXtyZXR1cm4gSi5pYShhKS53KGEpfSwKd2Y6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gSi5ZRShhKS5zUk4o
+YSxiKX0sCng5OmZ1bmN0aW9uKGEsYil7aWYodHlwZW9mIGI9PT0ibnVtYmVyIilpZihhLmNvbnN0cnVj
+dG9yPT1BcnJheXx8dHlwZW9mIGE9PSJzdHJpbmcifHxILndWKGEsYVt2LmRpc3BhdGNoUHJvcGVydHlO
+YW1lXSkpaWYoYj4+PjA9PT1iJiZiPGEubGVuZ3RoKXJldHVybiBhW2JdCnJldHVybiBKLlU2KGEpLnEo
+YSxiKX0sCnpsOmZ1bmN0aW9uKGEsYil7cmV0dXJuIEouVTYoYSkudGcoYSxiKX0sCkd2OmZ1bmN0aW9u
+IEd2KCl7fSwKeUU6ZnVuY3Rpb24geUUoKXt9LAp3ZTpmdW5jdGlvbiB3ZSgpe30sCk1GOmZ1bmN0aW9u
+IE1GKCl7fSwKaUM6ZnVuY3Rpb24gaUMoKXt9LAprZDpmdW5jdGlvbiBrZCgpe30sCmM1OmZ1bmN0aW9u
+IGM1KCl7fSwKamQ6ZnVuY3Rpb24gamQoYSl7dGhpcy4kdGk9YX0sClBvOmZ1bmN0aW9uIFBvKGEpe3Ro
+aXMuJHRpPWF9LAptMTpmdW5jdGlvbiBtMShhLGIsYyl7dmFyIF89dGhpcwpfLmE9YQpfLmI9YgpfLmM9
+MApfLmQ9bnVsbApfLiR0aT1jfSwKcUk6ZnVuY3Rpb24gcUkoKXt9LApiVTpmdW5jdGlvbiBiVSgpe30s
+CmtEOmZ1bmN0aW9uIGtEKCl7fSwKRHI6ZnVuY3Rpb24gRHIoKXt9fSxMPXsKSXE6ZnVuY3Rpb24oKXtD
+LkJaLkIoZG9jdW1lbnQsIkRPTUNvbnRlbnRMb2FkZWQiLG5ldyBMLmUoKSkKQy5vbC5CKHdpbmRvdywi
+cG9wc3RhdGUiLG5ldyBMLkwoKSl9LAprejpmdW5jdGlvbihhKXt2YXIgcyxyPXQuaC5hKGEucGFyZW50
+Tm9kZSkucXVlcnlTZWxlY3RvcigiOnNjb3BlID4gdWwiKSxxPXIuc3R5bGUscD0iIitDLkNELnpRKHIu
+b2Zmc2V0SGVpZ2h0KSoyKyJweCIKcS5tYXhIZWlnaHQ9cApxPUoucUYoYSkKcD1xLiR0aQpzPXAuQygi
+figxKT8iKS5hKG5ldyBMLld4KHIsYSkpCnQuWi5hKG51bGwpClcuSkUocS5hLHEuYixzLCExLHAuYyl9
+LAp5WDpmdW5jdGlvbihhLGIpe3ZhciBzLHIscSxwLG8sbixtPSJxdWVyeVNlbGVjdG9yQWxsIixsPWRv
+Y3VtZW50LnF1ZXJ5U2VsZWN0b3IoYSkKbC50b1N0cmluZwpzPXQuaApILkRoKHMscywiVCIsbSkKcj10
+LlUKcT1uZXcgVy53eihsLnF1ZXJ5U2VsZWN0b3JBbGwoIi5uYXYtbGluayIpLHIpCnEuSyhxLG5ldyBM
+LkFPKGIpKQpILkRoKHMscywiVCIsbSkKcD1uZXcgVy53eihsLnF1ZXJ5U2VsZWN0b3JBbGwoIi5yZWdp
+b24iKSxyKQppZighcC5nbDAocCkpe289bC5xdWVyeVNlbGVjdG9yKCJ0YWJsZVtkYXRhLXBhdGhdIikK
+by50b1N0cmluZwpwLksocCxuZXcgTC5IbyhvLmdldEF0dHJpYnV0ZSgiZGF0YS0iK25ldyBXLlN5KG5l
+dyBXLmk3KG8pKS5PVSgicGF0aCIpKSkpfUguRGgocyxzLCJUIixtKQpuPW5ldyBXLnd6KGwucXVlcnlT
+ZWxlY3RvckFsbCgiLmFkZC1oaW50LWxpbmsiKSxyKQpuLksobixuZXcgTC5JQygpKX0sClE2OmZ1bmN0
+aW9uKGEsYixjKXt2YXIgcz1uZXcgWE1MSHR0cFJlcXVlc3QoKQpDLkR0LmVvKHMsIkdFVCIsTC5RNChh
+LGIpLCEwKQpzLnNldFJlcXVlc3RIZWFkZXIoIkNvbnRlbnQtVHlwZSIsImFwcGxpY2F0aW9uL2pzb247
+IGNoYXJzZXQ9VVRGLTgiKQpyZXR1cm4gTC5MVShzLG51bGwsYyl9LAp0eTpmdW5jdGlvbihhLGIpe3Zh
+ciBzPW5ldyBYTUxIdHRwUmVxdWVzdCgpLHI9dC5OCkMuRHQuZW8ocywiUE9TVCIsTC5RNChhLFAuRmwo
+cixyKSksITApCnMuc2V0UmVxdWVzdEhlYWRlcigiQ29udGVudC1UeXBlIiwiYXBwbGljYXRpb24vanNv
+bjsgY2hhcnNldD1VVEYtOCIpCnJldHVybiBMLkxVKHMsYix0LkcpfSwKTFU6ZnVuY3Rpb24oYSxiLGMp
+e3JldHVybiBMLlRnKGEsYixjLGMuQygiMD8iKSl9LApUZzpmdW5jdGlvbihhLGIsYyxkKXt2YXIgcz0w
+LHI9UC5GWChkKSxxLHA9MixvLG49W10sbSxsLGssaixpLGgsZyxmLGUKdmFyICRhc3luYyRMVT1QLmx6
+KGZ1bmN0aW9uKGEwLGExKXtpZihhMD09PTEpe289YTEKcz1wfXdoaWxlKHRydWUpc3dpdGNoKHMpe2Nh
+c2UgMDpoPW5ldyBQLlpmKG5ldyBQLnZzKCQuWDMsdC5hbyksdC5iaikKZz10Lmd4CmY9Zy5hKG5ldyBM
+LmZDKGgsYSkpCnQuWi5hKG51bGwpCms9dC5wClcuSkUoYSwibG9hZCIsZiwhMSxrKQpXLkpFKGEsImVy
+cm9yIixnLmEoaC5nWUooKSksITEsaykKYS5zZW5kKGI9PW51bGw/bnVsbDpDLkN0Lk9CKGIsbnVsbCkp
+CnA9NApzPTcKcmV0dXJuIFAualEoaC5hLCRhc3luYyRMVSkKY2FzZSA3OnA9MgpzPTYKYnJlYWsKY2Fz
+ZSA0OnA9MwplPW8KSC5SdShlKQptPUgudHMoZSkKZz1hLnJlYWR5U3RhdGUKaWYoZz09PTQmJmEuc3Rh
+dHVzPT09MCl0aHJvdyBILmIoTC5URygiRXJyb3IgcmVhY2hpbmcgbWlncmF0aW9uIHByZXZpZXcgc2Vy
+dmVyIiwiVGhpcyB1c3VhbGx5IGhhcHBlbnMgYmVjYXVzZSB0aGUgbWlncmF0aW9uIHByZXZpZXcgc2Vy
+dmVyIGhhcyBleGl0ZWQuICBGb3JcbmV4YW1wbGUgaXQgbWF5IGhhdmUgYmVlbiBhYm9ydGVkIHdpdGgg
+Q3RybC1DLCBvciB5b3UgbWF5IGhhdmUgY29tcGxldGVkIHRoaXNcbm1pZ3JhdGlvbiwgb3IgYW4gZXhj
+ZXB0aW9uIG1heSBoYXZlIG9jY3VycmVkLiAgUGxlYXNlIGNoZWNrIHRoZSBjb25zb2xlIHdoZXJlXG55
+b3UgaW52b2tlZCBgZGFydCBtaWdyYXRlYCB0byB2ZXJpZnkgdGhhdCB0aGUgcHJldmlldyBzZXJ2ZXIg
+aXMgc3RpbGwgcnVubmluZy5cbiIpKQplbHNle2w9bmV3IEgubEooSC5RSShbInJlYWR5U3RhdGU9Iitn
+LCJyZXNwb25zZVRleHQ9IitDLkN0Lk9CKGEucmVzcG9uc2VUZXh0LG51bGwpLCJyZXNwb25zZVR5cGU9
+IitDLkN0Lk9CKGEucmVzcG9uc2VUeXBlLG51bGwpLCJyZXNwb25zZVVybD0iK0MuQ3QuT0IoYS5yZXNw
+b25zZVVSTCxudWxsKSwic3RhdHVzPSIrSC5FaihhLnN0YXR1cyksInN0YXR1c1RleHQ9IitDLkN0Lk9C
+KGEuc3RhdHVzVGV4dCxudWxsKV0sdC5zKSx0LmRHLmEobmV3IEwuVG0oKSksdC5EKS5rKDAsIiwgIikK
+dGhyb3cgSC5iKFAuVGwoIkVycm9yIHJlYWNoaW5nIG1pZ3JhdGlvbiBwcmV2aWV3IHNlcnZlcjogIitI
+LkVqKGwpLG0pKX1zPTYKYnJlYWsKY2FzZSAzOnM9MgpicmVhawpjYXNlIDY6aWYoYS5zdGF0dXM9PT00
+MDEpdGhyb3cgSC5iKEwuVEcoIlVuYXV0aG9yaXplZCByZXNwb25zZSBmcm9tIG1pZ3JhdGlvbiBwcmV2
+aWV3IHNlcnZlciIsIlRoZSBtaWdyYXRpb24gcHJldmlldyBzZXJ2ZXIgaGFzIGRldGVjdGVkIGEgbWlz
+bWF0Y2ggYmV0d2VlbiB0aGUgYXV0aFRva2VuIGluXG55b3VyIFVSTCBhbmQgdGhlIHRva2VuIHRoYXQg
+d2FzIGdlbmVyYXRlZCBhdCB0aGUgdGltZSB0aGF0IGBkYXJ0IG1pZ3JhdGVgIHdhc1xucnVuLiAgSGF2
+ZSB5b3UgcmVzdGFydGVkIHRoZSBtaWdyYXRpb24gc2VydmVyIHJlY2VudGx5PyAgSWYgc28sIHlvdSds
+bCBuZWVkIHRvXG5jaGVjayBpdHMgb3V0cHV0IGZvciBhIGZyZXNoIFVSTCwgYW5kIHVzZSB0aGF0IFVS
+TCB0byBwZXJmb3JtIHlvdXIgbWlncmF0aW9uLlxuIikpCmc9YS5yZXNwb25zZVRleHQKZy50b1N0cmlu
+ZwppPUMuQ3QucFcoMCxnLG51bGwpCmlmKGEuc3RhdHVzPT09MjAwKXtxPWMuQygiMD8iKS5hKGkpCnM9
+MQpicmVha31lbHNle2kudG9TdHJpbmcKdGhyb3cgSC5iKGkpfWNhc2UgMTpyZXR1cm4gUC55QyhxLHIp
+CmNhc2UgMjpyZXR1cm4gUC5mMyhvLHIpfX0pCnJldHVybiBQLkRJKCRhc3luYyRMVSxyKX0sCmFLOmZ1
+bmN0aW9uKGEpe3ZhciBzPVAuaEsoYSkuZ2hZKCkucSgwLCJsaW5lIikKcmV0dXJuIHM9PW51bGw/bnVs
+bDpILkhwKHMsbnVsbCl9LApHNjpmdW5jdGlvbihhKXt2YXIgcz1QLmhLKGEpLmdoWSgpLnEoMCwib2Zm
+c2V0IikKcmV0dXJuIHM9PW51bGw/bnVsbDpILkhwKHMsbnVsbCl9LAppNjpmdW5jdGlvbihhKXtyZXR1
+cm4gTC5uVyh0LlYuYShhKSl9LApuVzpmdW5jdGlvbihhKXt2YXIgcz0wLHI9UC5GWCh0LnopLHE9MSxw
+LG89W10sbixtLGwsayxqLGksaCxnCnZhciAkYXN5bmMkaTY9UC5seihmdW5jdGlvbihiLGMpe2lmKGI9
+PT0xKXtwPWMKcz1xfXdoaWxlKHRydWUpc3dpdGNoKHMpe2Nhc2UgMDpoPXQuaC5hKFcucWMoYS5jdXJy
+ZW50VGFyZ2V0KSkuZ2V0QXR0cmlidXRlKCJocmVmIikKaC50b1N0cmluZwpuPWgKYS5wcmV2ZW50RGVm
+YXVsdCgpCnE9MwpoPWRvY3VtZW50Cm09Qy5DRC56UShoLnF1ZXJ5U2VsZWN0b3IoIi5jb250ZW50Iiku
+c2Nyb2xsVG9wKQpzPTYKcmV0dXJuIFAualEoTC50eShuLG51bGwpLCRhc3luYyRpNikKY2FzZSA2Omo9
+dC5GLmEod2luZG93LmxvY2F0aW9uKS5wYXRobmFtZQpqLnRvU3RyaW5nCnM9NwpyZXR1cm4gUC5qUShM
+Lkc3KGosbnVsbCxudWxsLCExLG51bGwpLCRhc3luYyRpNikKY2FzZSA3OmguYm9keS5jbGFzc0xpc3Qu
+YWRkKCJuZWVkcy1yZXJ1biIpCmg9aC5xdWVyeVNlbGVjdG9yKCIuY29udGVudCIpCmgudG9TdHJpbmcK
+aC5zY3JvbGxUb3A9Qy5qbi56UShtKQpxPTEKcz01CmJyZWFrCmNhc2UgMzpxPTIKZz1wCmw9SC5SdShn
+KQprPUgudHMoZykKTC5DMigiY291bGRuJ3QgYWRkL3JlbW92ZSBoaW50IixsLGspCnM9NQpicmVhawpj
+YXNlIDI6cz0xCmJyZWFrCmNhc2UgNTpyZXR1cm4gUC55QyhudWxsLHIpCmNhc2UgMTpyZXR1cm4gUC5m
+MyhwLHIpfX0pCnJldHVybiBQLkRJKCRhc3luYyRpNixyKX0sCkMyOmZ1bmN0aW9uKGEsYixjKXt2YXIg
+cyxyLHEscCxvLG4sbT0iZXhjZXB0aW9uIixsPSJzdGFja1RyYWNlIgppZih0Lmg2LmIoYikmJkouUk0o
+Yi5xKDAsInN1Y2Nlc3MiKSwhMSkmJmIueDQobSkmJmIueDQobCkpe3M9Si5VNihiKQpyPUguayhzLnEo
+YixtKSkKYz1zLnEoYixsKQpxPW51bGx9ZWxzZSBpZihiIGluc3RhbmNlb2YgTC5RVyl7cj1iLmEKcT1i
+LmJ9ZWxzZXtyPUoudyhiKQpxPW51bGx9aWYocT09bnVsbClxPWMKcz1kb2N1bWVudApwPXMucXVlcnlT
+ZWxlY3RvcigiLnBvcHVwLXBhbmUiKQpwLnF1ZXJ5U2VsZWN0b3IoImgyIikuaW5uZXJUZXh0PWEKbz1w
+LnF1ZXJ5U2VsZWN0b3IoInAiKQpvLnRvU3RyaW5nCnIudG9TdHJpbmcKby5pbm5lclRleHQ9cgpvPXAu
+cXVlcnlTZWxlY3RvcigicHJlIikKby50b1N0cmluZwpvLmlubmVyVGV4dD1KLncocSkKbj10LmJxLmEo
+cC5xdWVyeVNlbGVjdG9yKCJhLmJvdHRvbSIpKQpDLnhuLnNMVShuLFAuWGQoImh0dHBzIiwiZ2l0aHVi
+LmNvbSIsImRhcnQtbGFuZy9zZGsvaXNzdWVzL25ldyIsUC5FRihbInRpdGxlIiwiQ3VzdG9tZXItcmVw
+b3J0ZWQgaXNzdWUgd2l0aCBOTkJEIG1pZ3JhdGlvbiB0b29sOiAiK2EsImxhYmVscyIsdS5kLCJib2R5
+IixhKyJcblxuRXJyb3I6ICIrcisiXG5cblBsZWFzZSBmaWxsIGluIHRoZSBmb2xsb3dpbmc6XG5cbioq
+TmFtZSBvZiBwYWNrYWdlIGJlaW5nIG1pZ3JhdGVkIChpZiBwdWJsaWMpKio6XG4qKldoYXQgSSB3YXMg
+ZG9pbmcgd2hlbiB0aGlzIGlzc3VlIG9jY3VycmVkKio6XG4qKklzIGl0IHBvc3NpYmxlIHRvIHdvcmsg
+YXJvdW5kIHRoaXMgaXNzdWUqKjpcbioqSGFzIHRoaXMgaXNzdWUgaGFwcGVuZWQgYmVmb3JlLCBhbmQg
+aWYgc28sIGhvdyBvZnRlbioqOlxuKipEYXJ0IFNESyB2ZXJzaW9uKio6ICIrSC5FaihzLmdldEVsZW1l
+bnRCeUlkKCJzZGstdmVyc2lvbiIpLnRleHRDb250ZW50KSsiXG4qKkFkZGl0aW9uYWwgZGV0YWlscyoq
+OlxuXG5UaGFua3MgZm9yIGZpbGluZyFcblxuU3RhY2t0cmFjZTogX2F1dG8gcG9wdWxhdGVkIGJ5IG1p
+Z3JhdGlvbiBwcmV2aWV3IHRvb2wuX1xuXG5gYGBcbiIrSC5FaihjKSsiXG5gYGBcbiJdLHQuTix0Lnop
+KS5nbkQoKSkKcz1uLnN0eWxlCnMuZGlzcGxheT0iaW5pdGlhbCIKcz1wLnN0eWxlCnMuZGlzcGxheT0i
+aW5pdGlhbCIKcz1hKyI6ICIrSC5FaihiKQp3aW5kb3cKaWYodHlwZW9mIGNvbnNvbGUhPSJ1bmRlZmlu
+ZWQiKXdpbmRvdy5jb25zb2xlLmVycm9yKHMpCndpbmRvdwpzPUguRWooYykKaWYodHlwZW9mIGNvbnNv
+bGUhPSJ1bmRlZmluZWQiKXdpbmRvdy5jb25zb2xlLmVycm9yKHMpfSwKdDI6ZnVuY3Rpb24oYSxiKXt2
+YXIgcyxyLHEscCxvPXQuaC5hKFcucWMoYS5jdXJyZW50VGFyZ2V0KSkKYS5wcmV2ZW50RGVmYXVsdCgp
+CnM9by5nZXRBdHRyaWJ1dGUoImhyZWYiKQpzLnRvU3RyaW5nCnI9TC5VcyhzKQpxPUwuRzYocykKcD1M
+LmFLKHMpCmlmKHEhPW51bGwpTC5hZihyLHEscCxiLG5ldyBMLm5UKHIscSxwKSkKZWxzZSBMLmFmKHIs
+bnVsbCxudWxsLGIsbmV3IEwuTlkocikpfSwKSzA6ZnVuY3Rpb24oYSl7dmFyIHMscixxLHA9ZG9jdW1l
+bnQucXVlcnlTZWxlY3RvcigiLnBvcHVwLXBhbmUiKQpwLnF1ZXJ5U2VsZWN0b3IoImgyIikuaW5uZXJU
+ZXh0PSJGYWlsZWQgdG8gcmVydW4gZnJvbSBzb3VyY2VzIgpwLnF1ZXJ5U2VsZWN0b3IoInAiKS5pbm5l
+clRleHQ9IlNvdXJjZXMgY29udGFpbiBzdGF0aWMgYW5hbHlzaXMgZXJyb3JzOiIKcz1wLnF1ZXJ5U2Vs
+ZWN0b3IoInByZSIpCnMudG9TdHJpbmcKcj1KLkVsKGEsdC5mKQpxPUguTGgocikKcy5pbm5lclRleHQ9
+bmV3IEgubEoocixxLkMoInFVKGxELkUpIikuYShuZXcgTC51ZSgpKSxxLkMoImxKPGxELkUscVU+Iikp
+LmsoMCwiXG4iKQpxPXAucXVlcnlTZWxlY3RvcigiYS5ib3R0b20iKS5zdHlsZQpxLmRpc3BsYXk9Im5v
+bmUiCnM9cC5zdHlsZQpzLmRpc3BsYXk9ImluaXRpYWwifSwKdlU6ZnVuY3Rpb24oKXt2YXIgcz1kb2N1
+bWVudCxyPXQuaApILkRoKHIsciwiVCIsInF1ZXJ5U2VsZWN0b3JBbGwiKQpzPW5ldyBXLnd6KHMucXVl
+cnlTZWxlY3RvckFsbCgiLmNvZGUiKSx0LlUpCnMuSyhzLG5ldyBMLkdIKCkpfSwKaFg6ZnVuY3Rpb24o
+YSxiLGMpe3JldHVybiBMLll3KGEsYixjKX0sCll3OmZ1bmN0aW9uKGEsYixjKXt2YXIgcz0wLHI9UC5G
+WCh0LnopLHE9MSxwLG89W10sbixtLGwsayxqLGksaCxnCnZhciAkYXN5bmMkaFg9UC5seihmdW5jdGlv
+bihkLGUpe2lmKGQ9PT0xKXtwPWUKcz1xfXdoaWxlKHRydWUpc3dpdGNoKHMpe2Nhc2UgMDpxPTMKaj10
+Lk4Kcz02CnJldHVybiBQLmpRKEwuUTYoYSxQLkVGKFsicmVnaW9uIiwicmVnaW9uIiwib2Zmc2V0IixI
+LkVqKGIpXSxqLGopLHQuRyksJGFzeW5jJGhYKQpjYXNlIDY6bj1lCmo9bgppPUouVTYoaikKbT1uZXcg
+VS5kMihVLmpmKGkucShqLCJlZGl0cyIpKSxILmsoaS5xKGosImV4cGxhbmF0aW9uIikpLEguVWMoaS5x
+KGosImxpbmUiKSksSC5rKGkucShqLCJkaXNwbGF5UGF0aCIpKSxILmsoaS5xKGosInVyaVBhdGgiKSks
+VS5OZChpLnEoaiwidHJhY2VzIikpKQpMLlQxKG0pCkwuRnIoYSxiLGMpCkwueVgoIi5lZGl0LXBhbmVs
+IC5wYW5lbC1jb250ZW50IiwhMSkKcT0xCnM9NQpicmVhawpjYXNlIDM6cT0yCmc9cApsPUguUnUoZykK
+az1ILnRzKGcpCkwuQzIoImNvdWxkbid0IGxvYWQgZWRpdCBkZXRhaWxzIixsLGspCnM9NQpicmVhawpj
+YXNlIDI6cz0xCmJyZWFrCmNhc2UgNTpyZXR1cm4gUC55QyhudWxsLHIpCmNhc2UgMTpyZXR1cm4gUC5m
+MyhwLHIpfX0pCnJldHVybiBQLkRJKCRhc3luYyRoWCxyKX0sCkc3OmZ1bmN0aW9uKGEsYixjLGQsZSl7
+cmV0dXJuIEwuTDUoYSxiLGMsZCxlKX0sCkw1OmZ1bmN0aW9uKGEsYixjLGQsZSl7dmFyIHM9MCxyPVAu
+RlgodC5IKSxxLHA9MixvLG49W10sbSxsLGssaixpLGgsZwp2YXIgJGFzeW5jJEc3PVAubHooZnVuY3Rp
+b24oZixhMCl7aWYoZj09PTEpe289YTAKcz1wfXdoaWxlKHRydWUpc3dpdGNoKHMpe2Nhc2UgMDppZigh
+Qy54Qi5UYyhhLCIuZGFydCIpKXtMLkJFKGEsQi53UigpLGQpCkwuQlgoYSxudWxsKQppZihlIT1udWxs
+KWUuJDAoKQpzPTEKYnJlYWt9cD00Cmk9dC5OCnM9NwpyZXR1cm4gUC5qUShMLlE2KGEsUC5FRihbImlu
+bGluZSIsInRydWUiXSxpLGkpLHQuRyksJGFzeW5jJEc3KQpjYXNlIDc6bT1hMApMLkJFKGEsQi5ZZiht
+KSxkKQpMLmZHKGIsYykKbD1MLlVzKGEpCkwuQlgobCxiKQppZihlIT1udWxsKWUuJDAoKQpwPTIKcz02
+CmJyZWFrCmNhc2UgNDpwPTMKZz1vCms9SC5SdShnKQpqPUgudHMoZykKTC5DMigiY291bGRuJ3QgbG9h
+ZCBkYXJ0IGZpbGUgIithLGssaikKcz02CmJyZWFrCmNhc2UgMzpzPTIKYnJlYWsKY2FzZSA2OmNhc2Ug
+MTpyZXR1cm4gUC55QyhxLHIpCmNhc2UgMjpyZXR1cm4gUC5mMyhvLHIpfX0pCnJldHVybiBQLkRJKCRh
+c3luYyRHNyxyKX0sCkdlOmZ1bmN0aW9uKCl7dmFyIHM9MCxyPVAuRlgodC56KSxxPTEscCxvPVtdLG4s
+bSxsLGssaixpLGgsZwp2YXIgJGFzeW5jJEdlPVAubHooZnVuY3Rpb24oYSxiKXtpZihhPT09MSl7cD1i
+CnM9cX13aGlsZSh0cnVlKXN3aXRjaChzKXtjYXNlIDA6aD0iL19wcmV2aWV3L25hdmlnYXRpb25UcmVl
+Lmpzb24iCnE9MwpzPTYKcmV0dXJuIFAualEoTC5RNihoLEMuQ00sdC5lZSksJGFzeW5jJEdlKQpjYXNl
+IDY6bj1iCmo9ZG9jdW1lbnQucXVlcnlTZWxlY3RvcigiLm5hdi10cmVlIikKai50b1N0cmluZwptPWoK
+Si5sNShtLCIiKQpqPUwubUsobikKJC5JUj1qCkwudFgobSxqLCEwKQpxPTEKcz01CmJyZWFrCmNhc2Ug
+MzpxPTIKZz1wCmw9SC5SdShnKQprPUgudHMoZykKTC5DMigiY291bGRuJ3QgbG9hZCBuYXZpZ2F0aW9u
+IHRyZWUiLGwsaykKcz01CmJyZWFrCmNhc2UgMjpzPTEKYnJlYWsKY2FzZSA1OnJldHVybiBQLnlDKG51
+bGwscikKY2FzZSAxOnJldHVybiBQLmYzKHAscil9fSkKcmV0dXJuIFAuREkoJGFzeW5jJEdlLHIpfSwK
+cU86ZnVuY3Rpb24oYSl7dmFyIHMscixxPWEuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkscD1DLkNELnpR
+KCQuZmkoKS5vZmZzZXRIZWlnaHQpLG89d2luZG93LmlubmVySGVpZ2h0Cm8udG9TdHJpbmcKcz1DLkNE
+LnpRKCQuRFcoKS5vZmZzZXRIZWlnaHQpCnI9cS5ib3R0b20Kci50b1N0cmluZwppZihyPm8tKHMrMTQp
+KUouZGgoYSkKZWxzZXtvPXEudG9wCm8udG9TdHJpbmcKaWYobzxwKzE0KUouZGgoYSl9fSwKZkc6ZnVu
+Y3Rpb24oYSxiKXt2YXIgcyxyLHEscCxvCmlmKGEhPW51bGwpe3M9ZG9jdW1lbnQKcj1zLmdldEVsZW1l
+bnRCeUlkKCJvIitILkVqKGEpKQpxPXMucXVlcnlTZWxlY3RvcigiLmxpbmUtIitILkVqKGIpKQppZihy
+IT1udWxsKXtMLnFPKHIpCkouZFIocikuaSgwLCJ0YXJnZXQiKX1lbHNlIGlmKHEhPW51bGwpe3M9cS5w
+YXJlbnRFbGVtZW50CnMudG9TdHJpbmcKTC5xTyhzKX1pZihxIT1udWxsKUouZFIodC5oLmEocS5wYXJl
+bnROb2RlKSkuaSgwLCJoaWdobGlnaHQiKX1lbHNle3M9ZG9jdW1lbnQKcD10LmgKSC5EaChwLHAsIlQi
LCJxdWVyeVNlbGVjdG9yQWxsIikKcz1zLnF1ZXJ5U2VsZWN0b3JBbGwoIi5saW5lLW5vIikKbz1uZXcg
-Vy53eihzLHQuUikKaWYoby5nQShvKT09PTApcmV0dXJuCkwucU8ocC5hKEMudDUuZ3RIKHMpKSl9fSwK
-YWY6ZnVuY3Rpb24oYSxiLGMsZCxlKXt2YXIgcyxyLHE9TC5HNih3aW5kb3cubG9jYXRpb24uaHJlZiks
-cD1MLmFLKHdpbmRvdy5sb2NhdGlvbi5ocmVmKQppZihxIT1udWxsKXtzPWRvY3VtZW50LmdldEVsZW1l
-bnRCeUlkKCJvIitILkVqKHEpKQppZihzIT1udWxsKUouZFIocykuUigwLCJ0YXJnZXQiKX1pZihwIT1u
-dWxsKXtyPWRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoIi5saW5lLSIrSC5FaihwKSkKaWYociE9bnVsbClK
-LmRSKHIucGFyZW50RWxlbWVudCkuUigwLCJoaWdobGlnaHQiKX1pZihhPT13aW5kb3cubG9jYXRpb24u
-cGF0aG5hbWUpe0wuZkcoYixjKQplLiQwKCl9ZWxzZSBMLkc3KGEsYixjLGQsZSl9LApRNDpmdW5jdGlv
-bihhLGIpe3ZhciBzLHIscT1QLmhLKGEpLHA9dC5YCnA9UC5GbChwLHApCmZvcihzPXEuZ2hZKCkscz1z
-LmdQdShzKSxzPXMuZ20ocyk7cy5GKCk7KXtyPXMuZ2woKQpwLlk1KDAsci5hLHIuYil9Zm9yKHM9Yi5n
-UHUoYikscz1zLmdtKHMpO3MuRigpOyl7cj1zLmdsKCkKcC5ZNSgwLHIuYSxyLmIpfXAuWTUoMCwiYXV0
-aFRva2VuIiwkLlVFKCkpCnJldHVybiBxLm5tKDAscCkuZ25EKCl9LApUMTpmdW5jdGlvbihhKXt2YXIg
-cyxyLHEscCxvLG4sbSxsLGssaj0kLmhMKCkKSi5sNShqLCIiKQppZihhPT1udWxsKXtzPWRvY3VtZW50
-LmNyZWF0ZUVsZW1lbnQoInAiKQpDLkx0LnNhNChzLCJTZWUgZGV0YWlscyBhYm91dCBhIHByb3Bvc2Vk
-IGVkaXQuIikKQy5MdC5zUChzLEguUUkoWyJwbGFjZWhvbGRlciJdLHQuaSkpCmouYXBwZW5kQ2hpbGQo
-cykKQy5MdC5GRihzKQpyZXR1cm59cj1hLmQKcT0kLm5VKCkKcD1xLnpmKHIpCm89YS5iCm49ZG9jdW1l
-bnQKbT1xLkhQKHIsSi5UMChuLnF1ZXJ5U2VsZWN0b3IoIi5yb290IikudGV4dENvbnRlbnQpKQpsPWEu
-YwprPW4uY3JlYXRlRWxlbWVudCgicCIpCmouYXBwZW5kQ2hpbGQoaykKay5hcHBlbmRDaGlsZChuLmNy
-ZWF0ZVRleHROb2RlKEguRWoobykrIiBhdCAiKSkKcT10LlgKcT1XLko2KEwuUTQoYS5lLFAuRUYoWyJs
-aW5lIixKLncobCldLHEscSkpKQpxLmFwcGVuZENoaWxkKG4uY3JlYXRlVGV4dE5vZGUoSC5FaihtKSsi
-OiIrSC5FaihsKSsiLiIpKQprLmFwcGVuZENoaWxkKHEpCkouZGgoaykKTC5DQyhhLGoscCkKTC5Geihh
-LGopfSwKTEg6ZnVuY3Rpb24oYSxiLGMpe3ZhciBzLHIscSxwLG8sbixtLGwsayxqLGksaCxnLGYsZT0k
-LnlQKCkKSi5sNShlLCIiKQppZihiLmdBKGIpPT09MCl7cz1kb2N1bWVudApyPXMuY3JlYXRlRWxlbWVu
-dCgicCIpCmUuYXBwZW5kQ2hpbGQocikKci5hcHBlbmRDaGlsZChzLmNyZWF0ZVRleHROb2RlKCJObyBw
-cm9wb3NlZCBlZGl0cyIpKX1lbHNlIGZvcihlPWIuZ1B1KGIpLGU9ZS5nbShlKSxzPXQuWCxxPXQuayxw
-PXEuQygifigxKT8iKSxvPXQuWixxPXEuYztlLkYoKTspe249ZS5nbCgpCm09ZG9jdW1lbnQKcj1tLmNy
-ZWF0ZUVsZW1lbnQoInAiKQpsPSQueVAoKQpsLmFwcGVuZENoaWxkKHIpCnIuYXBwZW5kQ2hpbGQobS5j
-cmVhdGVUZXh0Tm9kZShILkVqKG4uYSkrIjoiKSkKaz1tLmNyZWF0ZUVsZW1lbnQoInVsIikKbC5hcHBl
-bmRDaGlsZChrKQpmb3Iobj1KLklUKG4uYik7bi5GKCk7KXtsPW4uZ2woKQpqPW0uY3JlYXRlRWxlbWVu
-dCgibGkiKQprLmFwcGVuZENoaWxkKGopCkouZFIoaikuaSgwLCJlZGl0IikKaT1tLmNyZWF0ZUVsZW1l
-bnQoImEiKQpqLmFwcGVuZENoaWxkKGkpCmkuY2xhc3NMaXN0LmFkZCgiZWRpdC1saW5rIikKaD1sLmMK
-Zz1ILkVqKGgpCmkuc2V0QXR0cmlidXRlKCJkYXRhLSIrbmV3IFcuU3kobmV3IFcuaTcoaSkpLk9VKCJv
-ZmZzZXQiKSxnKQpmPWwuYQpnPUguRWooZikKaS5zZXRBdHRyaWJ1dGUoImRhdGEtIituZXcgVy5TeShu
-ZXcgVy5pNyhpKSkuT1UoImxpbmUiKSxnKQppLmFwcGVuZENoaWxkKG0uY3JlYXRlVGV4dE5vZGUoImxp
-bmUgIitILkVqKGYpKSkKaS5zZXRBdHRyaWJ1dGUoImhyZWYiLEwuUTQod2luZG93LmxvY2F0aW9uLnBh
-dGhuYW1lLFAuRUYoWyJsaW5lIixILkVqKGYpLCJvZmZzZXQiLEguRWooaCldLHMscykpKQpnPXAuYShu
-ZXcgTC5FRShoLGYsYSkpCm8uYShudWxsKQpXLkpFKGksImNsaWNrIixnLCExLHEpCmouYXBwZW5kQ2hp
-bGQobS5jcmVhdGVUZXh0Tm9kZSgiOiAiK0guRWoobC5iKSkpfX1pZihjKUwuVDEobnVsbCl9LApGcjpm
-dW5jdGlvbihhLGIsYyl7dmFyIHMscixxPXdpbmRvdy5sb2NhdGlvbixwPVAuaEsoKHEmJkMuRXgpLmdE
-cihxKStILkVqKGEpKQpxPXQuWApxPVAuRmwocSxxKQppZihiIT1udWxsKXEuWTUoMCwib2Zmc2V0IixI
-LkVqKGIpKQppZihjIT1udWxsKXEuWTUoMCwibGluZSIsSC5FaihjKSkKcS5ZNSgwLCJhdXRoVG9rZW4i
-LCQuVUUoKSkKcD1wLm5tKDAscSkKcT13aW5kb3cuaGlzdG9yeQpzPXQuegpyPXAuZ25EKCkKcS50b1N0
-cmluZwpxLnB1c2hTdGF0ZShuZXcgUC5CZihbXSxbXSkuUHYoUC5GbChzLHMpKSwiIixyKX0sCkVuOmZ1
-bmN0aW9uKGEpe3ZhciBzPUoucGIoZG9jdW1lbnQucXVlcnlTZWxlY3RvcigiLnJvb3QiKS50ZXh0Q29u
-dGVudCwiLyIpCmlmKEMueEIubkMoYSxzKSlyZXR1cm4gQy54Qi55bihhLHMubGVuZ3RoKQplbHNlIHJl
-dHVybiBhfSwKT3Q6ZnVuY3Rpb24oYSl7c3dpdGNoKGEucil7Y2FzZSBDLmN3OmJyZWFrCmNhc2UgQy5X
-RDphLnI9Qy5YagpicmVhawpjYXNlIEMuWGo6YS5yPUMuV0QKYnJlYWsKY2FzZSBDLmRjOnRocm93IEgu
-YihQLlBWKCJGaWxlICIrSC5FaihhLmMpKyIgc2hvdWxkIG5vdCBoYXZlIGluZGV0ZXJtaW5hdGUgbWln
-cmF0aW9uIHN0YXR1cyIpKX19LAp0YTpmdW5jdGlvbihhLGIpe3ZhciBzLHI9ImNoZWNrX2JveCIscT0i
-dGl0bGUiLHA9Im9wdGVkLW91dCIsbz0ibWlncmF0aW5nIgpzd2l0Y2goYil7Y2FzZSBDLmN3OmEuaW5u
-ZXJUZXh0PXIKcz1KLllFKGEpCnMuZ1AoYSkuaSgwLCJhbHJlYWR5LW1pZ3JhdGVkIikKcy5nUChhKS5p
-KDAsImRpc2FibGVkIikKYS5zZXRBdHRyaWJ1dGUocSwiQWxyZWFkeSBtaWdyYXRlZCIpCmJyZWFrCmNh
-c2UgQy5XRDphLmlubmVyVGV4dD1yCnM9Si5ZRShhKQpzLmdQKGEpLlIoMCxwKQpzLmdQKGEpLmkoMCxv
-KQphLnNldEF0dHJpYnV0ZShxLCJNaWdyYXRpbmcgdG8gbnVsbCBzYWZldHkiKQpicmVhawpjYXNlIEMu
-WGo6YS5pbm5lclRleHQ9ImNoZWNrX2JveF9vdXRsaW5lX2JsYW5rIgpzPUouWUUoYSkKcy5nUChhKS5S
-KDAsbykKcy5nUChhKS5pKDAscCkKYS5zZXRBdHRyaWJ1dGUocSwiT3B0aW5nIG91dCBvZiBudWxsIHNh
-ZmV0eSIpCmJyZWFrCmRlZmF1bHQ6YS5pbm5lclRleHQ9ImluZGV0ZXJtaW5hdGVfY2hlY2tfYm94Igpz
-PUouWUUoYSkKcy5nUChhKS5SKDAsbykKcy5nUChhKS5pKDAscCkKYS5zZXRBdHRyaWJ1dGUocSwiTWl4
-ZWQgc3RhdHVzZXMgb2YgJ21pZ3JhdGluZycgYW5kICdvcHRpbmcgb3V0JyIpCmJyZWFrfX0sCnhuOmZ1
-bmN0aW9uKGEsYil7dmFyIHMscixxPSJkaXNhYmxlZCIscD1iLmdMKCkKTC50YShhLHApCmlmKGIuYz09
-JC5EOSgpLmlubmVyVGV4dCl7cz1iIGluc3RhbmNlb2YgTC5jRCYmIUgub1QoYi54KQpyPUouWUUoYSkK
-aWYocylyLmdQKGEpLmkoMCxxKQplbHNlIHIuZ1AoYSkuUigwLHEpCkwudGEoJC5jMCgpLHApfX0sCkJY
-OmZ1bmN0aW9uKGEsYil7dmFyIHMscixxPXt9CnEuYT1hCmE9TC5FbihhKQpxLmE9YQpKLmRyKCQuRDko
-KSxhKQpzPWRvY3VtZW50CkguRGgodC5nLHQuaCwiVCIsInF1ZXJ5U2VsZWN0b3JBbGwiKQpzPW5ldyBX
-Lnd6KHMucXVlcnlTZWxlY3RvckFsbCgiLm5hdi1wYW5lbCAubmF2LWxpbmsiKSx0LlIpCnMuSyhzLG5l
-dyBMLlZTKHEpKQpzPSQuSVIKcj1zPT1udWxsP251bGw6TC55dyhzLHEuYSkKaWYocj09bnVsbClKLmRS
-KCQuYk4oKSkuUigwLCJ2aXNpYmxlIikKZWxzZXtKLmRSKCQuYk4oKSkuaSgwLCJ2aXNpYmxlIikKTC50
-YSgkLmMwKCksci5nTCgpKX19LApBUjpmdW5jdGlvbihhLGIpe3ZhciBzLHIscT1iLmIKaWYocSE9bnVs
-bCl7cz10LmcKcj1zLmEocy5hKGEucGFyZW50Tm9kZSkucGFyZW50Tm9kZSkKTC54bihyLnF1ZXJ5U2Vs
-ZWN0b3IoIjpzY29wZSA+IC5zdGF0dXMtaWNvbiIpLHEpCkwuQVIocixxKX19LApiTDpmdW5jdGlvbihh
-LGIpe3ZhciBzLHIscSxwLG8sbixtPSI6c2NvcGUgPiAuc3RhdHVzLWljb24iCmZvcihzPWIuZCxyPXMu
-bGVuZ3RoLHE9dC5nLHA9MDtwPHMubGVuZ3RoO3MubGVuZ3RoPT09cnx8KDAsSC5saykocyksKytwKXtv
-PXNbcF0Kbj1hLnF1ZXJ5U2VsZWN0b3IoJ1tkYXRhLW5hbWUqPSInK0guRWooVy5MaihvLmMpKSsnIl0n
-KQppZihvIGluc3RhbmNlb2YgTC52dCl7TC5iTChuLG8pCkwueG4obi5xdWVyeVNlbGVjdG9yKG0pLGIp
-fWVsc2UgTC54bihxLmEobi5wYXJlbnROb2RlKS5xdWVyeVNlbGVjdG9yKG0pLG8pfX0sCkJFOmZ1bmN0
-aW9uKGEsYixjKXt2YXIgcz0iLnJlZ2lvbnMiLHI9ZG9jdW1lbnQscT1yLnF1ZXJ5U2VsZWN0b3Iocyks
-cD1yLnF1ZXJ5U2VsZWN0b3IoIi5jb2RlIikKSi50SChxLGIuYSwkLktHKCkpCkoudEgocCxiLmIsJC5L
-RygpKQpMLkxIKGEsYi5kLGMpCmlmKGIuYy5sZW5ndGg8MmU1KUwudlUoKQpMLnlYKCIuY29kZSIsITAp
-CkwueVgocywhMCl9LAp0WDpmdW5jdGlvbihhMSxhMixhMyl7dmFyIHMscixxLHAsbyxuLG0sbCxrLGos
-aSxoLGcsZixlLGQsYz0ibWF0ZXJpYWwtaWNvbnMiLGI9InN0YXR1cy1pY29uIixhPWRvY3VtZW50LGEw
-PWEuY3JlYXRlRWxlbWVudCgidWwiKQphMS5hcHBlbmRDaGlsZChhMCkKZm9yKHM9YTIubGVuZ3RoLHI9
-dC5YLHE9dC5aLHA9MDtwPGEyLmxlbmd0aDthMi5sZW5ndGg9PT1zfHwoMCxILmxrKShhMiksKytwKXtv
-PWEyW3BdCm49YS5jcmVhdGVFbGVtZW50KCJsaSIpCmEwLmFwcGVuZENoaWxkKG4pCmlmKG8gaW5zdGFu
-Y2VvZiBMLnZ0KXttPUouWUUobikKbS5nUChuKS5pKDAsImRpciIpCm4uc2V0QXR0cmlidXRlKCJkYXRh
-LSIrbmV3IFcuU3kobmV3IFcuaTcobikpLk9VKCJuYW1lIiksby5jKQpsPWEuY3JlYXRlRWxlbWVudCgi
-c3BhbiIpCm4uYXBwZW5kQ2hpbGQobCkKaz1KLllFKGwpCmsuZ1AobCkuaSgwLCJhcnJvdyIpCmsuc2hm
-KGwsIiYjeDI1QkM7IikKaj1hLmNyZWF0ZUVsZW1lbnQoInNwYW4iKQpKLmRSKGopLmkoMCxjKQpqLmlu
-bmVyVGV4dD0iZm9sZGVyX29wZW4iCm4uYXBwZW5kQ2hpbGQoaikKbi5hcHBlbmRDaGlsZChhLmNyZWF0
-ZVRleHROb2RlKG8uYSkpCkwudFgobixvLmQsITApCmk9YS5jcmVhdGVFbGVtZW50KCJzcGFuIikKaz1K
-LllFKGkpCmsuZ1AoaSkuaSgwLGMpCmkuaW5uZXJUZXh0PSJpbmRldGVybWluYXRlX2NoZWNrX2JveCIK
-ay5nUChpKS5pKDAsYikKTC54bihpLG8pCms9ay5nVmwoaSkKaD1rLiR0aQpnPWguQygifigxKT8iKS5h
-KG5ldyBMLlREKG8sbixpKSkKcS5hKG51bGwpClcuSkUoay5hLGsuYixnLCExLGguYykKbS5tSyhuLGks
-aikKTC5reihsKX1lbHNlIGlmKG8gaW5zdGFuY2VvZiBMLmNEKXtpPWEuY3JlYXRlRWxlbWVudCgic3Bh
-biIpCm09Si5ZRShpKQptLmdQKGkpLmkoMCxjKQppLmlubmVyVGV4dD0iIgptLmdQKGkpLmkoMCxiKQpr
-PUgub1Qoby54KQppZighayltLmdQKGkpLmkoMCwiZGlzYWJsZWQiKQpMLnhuKGksbykKaWYoayl7bT1t
-LmdWbChpKQprPW0uJHRpCmg9ay5DKCJ+KDEpPyIpLmEobmV3IEwuSWYobyxpLG4pKQpxLmEobnVsbCkK
-Vy5KRShtLmEsbS5iLGgsITEsay5jKX1uLmFwcGVuZENoaWxkKGkpCm09YS5jcmVhdGVFbGVtZW50KCJz
-cGFuIikKSi5kUihtKS5pKDAsYykKbS5pbm5lclRleHQ9Imluc2VydF9kcml2ZV9maWxlIgpuLmFwcGVu
-ZENoaWxkKG0pCmY9YS5jcmVhdGVFbGVtZW50KCJhIikKbi5hcHBlbmRDaGlsZChmKQptPUouWUUoZikK
-bS5nUChmKS5pKDAsIm5hdi1saW5rIikKZi5zZXRBdHRyaWJ1dGUoImRhdGEtIituZXcgVy5TeShuZXcg
-Vy5pNyhmKSkuT1UoIm5hbWUiKSxvLmMpCmYuc2V0QXR0cmlidXRlKCJocmVmIixMLlE0KG8uZCxQLkZs
-KHIscikpKQpmLmFwcGVuZENoaWxkKGEuY3JlYXRlVGV4dE5vZGUoby5hKSkKbT1tLmdWbChmKQprPW0u
-JHRpCmg9ay5DKCJ+KDEpPyIpLmEobmV3IEwudEIoKSkKcS5hKG51bGwpClcuSkUobS5hLG0uYixoLCEx
-LGsuYykKZT1vLmUKaWYodHlwZW9mIGUhPT0ibnVtYmVyIilyZXR1cm4gZS5vcygpCmlmKGU+MCl7ZD1h
-LmNyZWF0ZUVsZW1lbnQoInNwYW4iKQpuLmFwcGVuZENoaWxkKGQpCkouZFIoZCkuaSgwLCJlZGl0LWNv
-dW50IikKbT0iIitlKyIgIgppZihlPT09MSlrPSJwcm9wb3NlZCBlZGl0IgplbHNlIGs9InByb3Bvc2Vk
-IGVkaXRzIgpkLnNldEF0dHJpYnV0ZSgidGl0bGUiLG0raykKZC5hcHBlbmRDaGlsZChhLmNyZWF0ZVRl
-eHROb2RlKEMuam4udyhlKSkpfX19fSwKdXo6ZnVuY3Rpb24oYSxiLGMpe3ZhciBzPWRvY3VtZW50LHI9
-cy5jcmVhdGVFbGVtZW50KCJidXR0b24iKSxxPXQuayxwPXEuQygifigxKT8iKS5hKG5ldyBMLm0yKGEs
-YykpCnQuWi5hKG51bGwpClcuSkUociwiY2xpY2siLHAsITEscS5jKQpyLmFwcGVuZENoaWxkKHMuY3Jl
-YXRlVGV4dE5vZGUoUi5PWChhLmEpKSkKYi5hcHBlbmRDaGlsZChyKX0sCkZ6OmZ1bmN0aW9uKGEsYil7
-dmFyIHMscixxLHAsbyxuLG0sbCxrLGosaSxoPWEuYQppZihoPT1udWxsKXJldHVybgpzPWRvY3VtZW50
-CnI9cy5jcmVhdGVFbGVtZW50KCJwIikKcT1iLmFwcGVuZENoaWxkKHIpCnI9cy5jcmVhdGVFbGVtZW50
-KCJzcGFuIikKcD10LmkKSi5NdShyLEguUUkoWyJ0eXBlLWRlc2NyaXB0aW9uIl0scCkpCnIuYXBwZW5k
-Q2hpbGQocy5jcmVhdGVUZXh0Tm9kZSgiQWN0aW9ucyIpKQpxLmFwcGVuZENoaWxkKHIpCnEuYXBwZW5k
-Q2hpbGQocy5jcmVhdGVUZXh0Tm9kZSgiOiIpKQpvPXMuY3JlYXRlRWxlbWVudCgicCIpCmIuYXBwZW5k
-Q2hpbGQobykKZm9yKHI9aC5sZW5ndGgsbj10LlEsbT0wO208aC5sZW5ndGg7aC5sZW5ndGg9PT1yfHwo
-MCxILmxrKShoKSwrK20pe2w9aFttXQprPXMuY3JlYXRlRWxlbWVudCgiYSIpCm8uYXBwZW5kQ2hpbGQo
-aykKay5hcHBlbmRDaGlsZChzLmNyZWF0ZVRleHROb2RlKGwuYSkpCmsuc2V0QXR0cmlidXRlKCJocmVm
-IixsLmIpCmo9bi5hKEguUUkoWyJhZGQtaGludC1saW5rIiwiYmVmb3JlLWFwcGx5IiwiYnV0dG9uIl0s
-cCkpCmk9Si5kUihrKQppLlYxKDApCmkuRlYoMCxqKX19LApDQzpmdW5jdGlvbihhNCxhNSxhNil7dmFy
-IHMscixxLHAsbyxuLG0sbCxrLGosaSxoLGcsZixlLGQsYyxiLGEsYTAsYTEsYTIsYTMKZm9yKHM9YTQu
-ZixyPXMubGVuZ3RoLHE9dC5pLHA9dC5RLG89MDtvPHMubGVuZ3RoO3MubGVuZ3RoPT09cnx8KDAsSC5s
-aykocyksKytvKXtuPXNbb10KbT1kb2N1bWVudApsPW0uY3JlYXRlRWxlbWVudCgicCIpCms9cC5hKEgu
-UUkoWyJ0cmFjZSJdLHEpKQpqPUouZFIobCkKai5WMSgwKQpqLkZWKDAsaykKaT1hNS5hcHBlbmRDaGls
-ZChsKQpsPW0uY3JlYXRlRWxlbWVudCgic3BhbiIpCms9cC5hKEguUUkoWyJ0eXBlLWRlc2NyaXB0aW9u
-Il0scSkpCmo9Si5kUihsKQpqLlYxKDApCmouRlYoMCxrKQpsLmFwcGVuZENoaWxkKG0uY3JlYXRlVGV4
-dE5vZGUobi5hKSkKaS5hcHBlbmRDaGlsZChsKQppLmFwcGVuZENoaWxkKG0uY3JlYXRlVGV4dE5vZGUo
-IjoiKSkKbD1tLmNyZWF0ZUVsZW1lbnQoInVsIikKaz1wLmEoSC5RSShbInRyYWNlIl0scSkpCmo9Si5k
-UihsKQpqLlYxKDApCmouRlYoMCxrKQpoPWkuYXBwZW5kQ2hpbGQobCkKZm9yKGw9bi5iLGs9bC5sZW5n
-dGgsZz0wO2c8bC5sZW5ndGg7bC5sZW5ndGg9PT1rfHwoMCxILmxrKShsKSwrK2cpe2Y9bFtnXQplPW0u
-Y3JlYXRlRWxlbWVudCgibGkiKQpoLmFwcGVuZENoaWxkKGUpCmQ9bS5jcmVhdGVFbGVtZW50KCJzcGFu
-IikKYz1wLmEoSC5RSShbImZ1bmN0aW9uIl0scSkpCmo9Si5kUihkKQpqLlYxKDApCmouRlYoMCxjKQpj
-PWYuYgpMLldqKGQsYz09bnVsbD8idW5rbm93biI6YykKZS5hcHBlbmRDaGlsZChkKQpiPWYuYwppZihi
-IT1udWxsKXtlLmFwcGVuZENoaWxkKG0uY3JlYXRlVGV4dE5vZGUoIiAoIikpCmE9Yi5iCmEwPW0uY3Jl
-YXRlRWxlbWVudCgiYSIpCmEwLmFwcGVuZENoaWxkKG0uY3JlYXRlVGV4dE5vZGUoSC5FaihiLmMpKyI6
-IitILkVqKGEpKSkKYTAuc2V0QXR0cmlidXRlKCJocmVmIixiLmEpCmEwLmNsYXNzTGlzdC5hZGQoIm5h
-di1saW5rIikKZS5hcHBlbmRDaGlsZChhMCkKZS5hcHBlbmRDaGlsZChtLmNyZWF0ZVRleHROb2RlKCIp
-IikpfWUuYXBwZW5kQ2hpbGQobS5jcmVhdGVUZXh0Tm9kZSgiOiAiKSkKZD1mLmEKTC5XaihlLGQ9PW51
-bGw/InVua25vd24iOmQpCmQ9Zi5kCmlmKGQubGVuZ3RoIT09MCl7Yz1tLmNyZWF0ZUVsZW1lbnQoInAi
-KQphMT1wLmEoSC5RSShbImRyYXdlciIsImJlZm9yZS1hcHBseSJdLHEpKQpqPUouZFIoYykKai5WMSgw
-KQpqLkZWKDAsYTEpCmEyPWUuYXBwZW5kQ2hpbGQoYykKZm9yKGM9ZC5sZW5ndGgsYTM9MDthMzxkLmxl
-bmd0aDtkLmxlbmd0aD09PWN8fCgwLEgubGspKGQpLCsrYTMpTC51eihkW2EzXSxhMixiKX19fX0sClVz
-OmZ1bmN0aW9uKGEpe3JldHVybiBKLnpsKGEsIj8iKT9DLnhCLk5qKGEsMCxDLnhCLk9ZKGEsIj8iKSk6
-YX0sClRHOmZ1bmN0aW9uKGEsYil7cmV0dXJuIG5ldyBMLlFXKGEsYil9LAp5dzpmdW5jdGlvbihhLGIp
-e3ZhciBzLHIscSxwCmZvcihzPWEubGVuZ3RoLHI9MDtyPGEubGVuZ3RoO2EubGVuZ3RoPT09c3x8KDAs
-SC5saykoYSksKytyKXtxPWFbcl0KaWYocSBpbnN0YW5jZW9mIEwudnQpe3A9TC55dyhxLmQsYikKaWYo
-cCE9bnVsbClyZXR1cm4gcH1lbHNlIGlmKHEuYz09YilyZXR1cm4gcX1yZXR1cm4gbnVsbH0sCldqOmZ1
-bmN0aW9uKGEsYil7dmFyIHMscixxPUguUUkoYi5zcGxpdCgiLiIpLHQucykscD1DLk5tLmd0SChxKSxv
-PWRvY3VtZW50CmEuYXBwZW5kQ2hpbGQoby5jcmVhdGVUZXh0Tm9kZShwKSkKZm9yKHA9SC5xQyhxLDEs
-bnVsbCx0Lk4pLHA9bmV3IEguYTcocCxwLmdBKHApLHAuJHRpLkMoImE3PGFMLkU+IikpLHM9Si5ZRShh
-KTtwLkYoKTspe3I9cC5kCnMubnooYSwiYmVmb3JlZW5kIiwiJiM4MjAzOy4iLG51bGwsbnVsbCkKYS5h
-cHBlbmRDaGlsZChvLmNyZWF0ZVRleHROb2RlKHIpKX19LAplOmZ1bmN0aW9uIGUoKXt9LApWVzpmdW5j
-dGlvbiBWVyhhLGIsYyl7dGhpcy5hPWEKdGhpcy5iPWIKdGhpcy5jPWN9LApvWjpmdW5jdGlvbiBvWigp
-e30sCmpyOmZ1bmN0aW9uIGpyKCl7fSwKcWw6ZnVuY3Rpb24gcWwoKXt9LApIaTpmdW5jdGlvbiBIaSgp
-e30sCkJUOmZ1bmN0aW9uIEJUKCl7fSwKUFk6ZnVuY3Rpb24gUFkoKXt9LAp1ODpmdW5jdGlvbiB1OCgp
-e30sCkw6ZnVuY3Rpb24gTCgpe30sCld4OmZ1bmN0aW9uIFd4KGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9
-LApBTzpmdW5jdGlvbiBBTyhhKXt0aGlzLmE9YX0sCmROOmZ1bmN0aW9uIGROKGEpe3RoaXMuYT1hfSwK
-SG86ZnVuY3Rpb24gSG8oYSl7dGhpcy5hPWF9LAp4ejpmdW5jdGlvbiB4eihhLGIpe3RoaXMuYT1hCnRo
-aXMuYj1ifSwKSUM6ZnVuY3Rpb24gSUMoKXt9LApmQzpmdW5jdGlvbiBmQyhhLGIpe3RoaXMuYT1hCnRo
-aXMuYj1ifSwKVG06ZnVuY3Rpb24gVG0oKXt9LApuVDpmdW5jdGlvbiBuVChhLGIsYyl7dGhpcy5hPWEK
-dGhpcy5iPWIKdGhpcy5jPWN9LApOWTpmdW5jdGlvbiBOWShhKXt0aGlzLmE9YX0sCnVlOmZ1bmN0aW9u
-IHVlKCl7fSwKZVg6ZnVuY3Rpb24gZVgoKXt9LApFRTpmdW5jdGlvbiBFRShhLGIsYyl7dGhpcy5hPWEK
-dGhpcy5iPWIKdGhpcy5jPWN9LApRTDpmdW5jdGlvbiBRTChhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwK
-VlM6ZnVuY3Rpb24gVlMoYSl7dGhpcy5hPWF9LApURDpmdW5jdGlvbiBURChhLGIsYyl7dGhpcy5hPWEK
-dGhpcy5iPWIKdGhpcy5jPWN9LApJZjpmdW5jdGlvbiBJZihhLGIsYyl7dGhpcy5hPWEKdGhpcy5iPWIK
-dGhpcy5jPWN9LAp0QjpmdW5jdGlvbiB0Qigpe30sCm0yOmZ1bmN0aW9uIG0yKGEsYil7dGhpcy5hPWEK
-dGhpcy5iPWJ9LApRVzpmdW5jdGlvbiBRVyhhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKWEE6ZnVuY3Rp
-b24gWEEoKXt9LApaczpmdW5jdGlvbihhKXt2YXIgcyxyLHE9Si5VNihhKQppZihMLnAyKEguaChxLnEo
-YSwidHlwZSIpKSk9PT1DLlkyKXtzPUguaChxLnEoYSwibmFtZSIpKQpyPUguaChxLnEoYSwicGF0aCIp
-KQpxPXEucShhLCJzdWJ0cmVlIikKcT1uZXcgTC52dChxPT1udWxsP251bGw6TC5tSyhxKSxzLHIpCnEu
-TFYoKQpyZXR1cm4gcX1lbHNle3M9SC5oKHEucShhLCJuYW1lIikpCnI9SC5oKHEucShhLCJwYXRoIikp
-CnJldHVybiBuZXcgTC5jRChILmgocS5xKGEsImhyZWYiKSksSC51UChxLnEoYSwiZWRpdENvdW50Iikp
-LEgueTgocS5xKGEsIndhc0V4cGxpY2l0bHlPcHRlZE91dCIpKSxMLnZCKEgudVAocS5xKGEsIm1pZ3Jh
-dGlvblN0YXR1cyIpKSksSC55OChxLnEoYSwibWlncmF0aW9uU3RhdHVzQ2FuQmVDaGFuZ2VkIikpLHMs
-cil9fSwKbUs6ZnVuY3Rpb24oYSl7dmFyIHMscj1ILlFJKFtdLHQuY1EpCmZvcihzPUouSVQodC5VLmEo
-YSkpO3MuRigpOylyLnB1c2goTC5acyhzLmdsKCkpKQpyZXR1cm4gcn0sClZEOmZ1bmN0aW9uKGEpe3Zh
-ciBzLHIscT1ILlFJKFtdLHQuRykKZm9yKHM9YS5sZW5ndGgscj0wO3I8YS5sZW5ndGg7YS5sZW5ndGg9
-PT1zfHwoMCxILmxrKShhKSwrK3IpcS5wdXNoKGFbcl0uTHQoKSkKcmV0dXJuIHF9LAp2QjpmdW5jdGlv
-bihhKXtpZihhPT1udWxsKXJldHVybiBudWxsCmlmKGE+Pj4wIT09YXx8YT49NClyZXR1cm4gSC5PSChD
-LmwwLGEpCnJldHVybiBDLmwwW2FdfSwKcDI6ZnVuY3Rpb24oYSl7c3dpdGNoKGEpe2Nhc2UiZGlyZWN0
-b3J5IjpyZXR1cm4gQy5ZMgpjYXNlImZpbGUiOnJldHVybiBDLnJmCmRlZmF1bHQ6dGhyb3cgSC5iKFAu
-UFYoIlVucmVjb2duaXplZCBuYXZpZ2F0aW9uIHRyZWUgbm9kZSB0eXBlOiAiK0guRWooYSkpKX19LAp2
-dDpmdW5jdGlvbiB2dChhLGIsYyl7dmFyIF89dGhpcwpfLmQ9YQpfLmE9YgpfLmI9bnVsbApfLmM9Y30s
-CmNEOmZ1bmN0aW9uIGNEKGEsYixjLGQsZSxmLGcpe3ZhciBfPXRoaXMKXy5kPWEKXy5lPWIKXy5mPWMK
-Xy5yPWQKXy54PWUKXy5hPWYKXy5iPW51bGwKXy5jPWd9LApEODpmdW5jdGlvbiBEOCgpe30sCk85OmZ1
-bmN0aW9uIE85KGEpe3RoaXMuYj1hfSwKR2I6ZnVuY3Rpb24gR2IoYSxiKXt0aGlzLmE9YQp0aGlzLmI9
-Yn0sCklWOmZ1bmN0aW9uIElWKGEsYixjLGQpe3ZhciBfPXRoaXMKXy5kPWEKXy5lPWIKXy5mPWMKXy5y
-PWR9fSxNPXsKWUY6ZnVuY3Rpb24oYSxiKXt2YXIgcyxyLHEscCxvLG4sbSxsCmZvcihzPWIubGVuZ3Ro
-LHI9MTtyPHM7KytyKXtpZihiW3JdPT1udWxsfHxiW3ItMV0hPW51bGwpY29udGludWUKZm9yKDtzPj0x
-O3M9cSl7cT1zLTEKaWYoYltxXSE9bnVsbClicmVha31wPW5ldyBQLk0oIiIpCm89YSsiKCIKcC5hPW8K
-bj1ILnQ2KGIpCm09bi5DKCJuSDwxPiIpCmw9bmV3IEgubkgoYiwwLHMsbSkKbC5IZChiLDAscyxuLmMp
-Cm09bytuZXcgSC5sSihsLG0uQygicVUoYUwuRSkiKS5hKG5ldyBNLk5vKCkpLG0uQygibEo8YUwuRSxx
-VT4iKSkuaygwLCIsICIpCnAuYT1tCnAuYT1tKygiKTogcGFydCAiKyhyLTEpKyIgd2FzIG51bGwsIGJ1
-dCBwYXJ0ICIrcisiIHdhcyBub3QuIikKdGhyb3cgSC5iKFAueFkocC53KDApKSl9fSwKbEk6ZnVuY3Rp
-b24gbEkoYSl7dGhpcy5hPWF9LApxNzpmdW5jdGlvbiBxNygpe30sCk5vOmZ1bmN0aW9uIE5vKCl7fX0s
-Tz17ClJoOmZ1bmN0aW9uKCl7dmFyIHMscj1udWxsCmlmKFAudW8oKS5nRmkoKSE9PSJmaWxlIilyZXR1
-cm4gJC5FYigpCnM9UC51bygpCmlmKCFDLnhCLlRjKHMuZ0lpKHMpLCIvIikpcmV0dXJuICQuRWIoKQpp
-ZihQLktMKHIsImEvYiIscixyLHIscixyKS50NCgpPT09ImFcXGIiKXJldHVybiAkLktrKCkKcmV0dXJu
-ICQuYkQoKX0sCnpMOmZ1bmN0aW9uIHpMKCl7fX0sUD17CnhnOmZ1bmN0aW9uKCl7dmFyIHMscixxPXt9
-CmlmKHNlbGYuc2NoZWR1bGVJbW1lZGlhdGUhPW51bGwpcmV0dXJuIFAuRVgoKQppZihzZWxmLk11dGF0
-aW9uT2JzZXJ2ZXIhPW51bGwmJnNlbGYuZG9jdW1lbnQhPW51bGwpe3M9c2VsZi5kb2N1bWVudC5jcmVh
-dGVFbGVtZW50KCJkaXYiKQpyPXNlbGYuZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgic3BhbiIpCnEuYT1u
-dWxsCm5ldyBzZWxmLk11dGF0aW9uT2JzZXJ2ZXIoSC50UihuZXcgUC50aChxKSwxKSkub2JzZXJ2ZShz
-LHtjaGlsZExpc3Q6dHJ1ZX0pCnJldHVybiBuZXcgUC5oYShxLHMscil9ZWxzZSBpZihzZWxmLnNldElt
-bWVkaWF0ZSE9bnVsbClyZXR1cm4gUC55dCgpCnJldHVybiBQLnFXKCl9LApaVjpmdW5jdGlvbihhKXtz
-ZWxmLnNjaGVkdWxlSW1tZWRpYXRlKEgudFIobmV3IFAuVnModC5NLmEoYSkpLDApKX0sCm9BOmZ1bmN0
-aW9uKGEpe3NlbGYuc2V0SW1tZWRpYXRlKEgudFIobmV3IFAuRnQodC5NLmEoYSkpLDApKX0sCkJ6OmZ1
-bmN0aW9uKGEpe3QuTS5hKGEpClAuUU4oMCxhKX0sClFOOmZ1bmN0aW9uKGEsYil7dmFyIHM9bmV3IFAu
-VzMoKQpzLkNZKGEsYikKcmV0dXJuIHN9LApGWDpmdW5jdGlvbihhKXtyZXR1cm4gbmV3IFAuaWgobmV3
-IFAudnMoJC5YMyxhLkMoInZzPDA+IikpLGEuQygiaWg8MD4iKSl9LApESTpmdW5jdGlvbihhLGIpe2Eu
-JDIoMCxudWxsKQpiLmI9ITAKcmV0dXJuIGIuYX0sCmpROmZ1bmN0aW9uKGEsYil7UC5KZShhLGIpfSwK
-eUM6ZnVuY3Rpb24oYSxiKXtiLmFNKDAsYSl9LApmMzpmdW5jdGlvbihhLGIpe2IudzAoSC5SdShhKSxI
-LnRzKGEpKX0sCkplOmZ1bmN0aW9uKGEsYil7dmFyIHMscixxPW5ldyBQLldNKGIpLHA9bmV3IFAuU1go
-YikKaWYoYSBpbnN0YW5jZW9mIFAudnMpYS5RZChxLHAsdC56KQplbHNle3M9dC56CmlmKHQuZS5iKGEp
-KWEuU3EocSxwLHMpCmVsc2V7cj1uZXcgUC52cygkLlgzLHQuYykKci5hPTQKci5jPWEKci5RZChxLHAs
-cyl9fX0sCmx6OmZ1bmN0aW9uKGEpe3ZhciBzPWZ1bmN0aW9uKGIsYyl7cmV0dXJuIGZ1bmN0aW9uKGQs
-ZSl7d2hpbGUodHJ1ZSl0cnl7YihkLGUpCmJyZWFrfWNhdGNoKHIpe2U9cgpkPWN9fX0oYSwxKQpyZXR1
-cm4gJC5YMy5MaihuZXcgUC5HcyhzKSx0LkgsdC5TLHQueil9LApHUTpmdW5jdGlvbihhKXtyZXR1cm4g
-bmV3IFAuRnkoYSwxKX0sClRoOmZ1bmN0aW9uKCl7cmV0dXJuIEMud1F9LApZbTpmdW5jdGlvbihhKXty
-ZXR1cm4gbmV3IFAuRnkoYSwzKX0sCmwwOmZ1bmN0aW9uKGEsYil7cmV0dXJuIG5ldyBQLnE0KGEsYi5D
-KCJxNDwwPiIpKX0sClRsOmZ1bmN0aW9uKGEsYil7dmFyIHM9SC5jYihhLCJlcnJvciIsdC5LKQpyZXR1
-cm4gbmV3IFAuQ3cocyxiPT1udWxsP1AudjAoYSk6Yil9LAp2MDpmdW5jdGlvbihhKXt2YXIgcwppZih0
-LnIuYihhKSl7cz1hLmdJSSgpCmlmKHMhPW51bGwpcmV0dXJuIHN9cmV0dXJuIEMucGR9LApBOTpmdW5j
-dGlvbihhLGIpe3ZhciBzLHIscQpmb3Iocz10LmM7cj1hLmEscj09PTI7KWE9cy5hKGEuYykKaWYocj49
-NCl7cT1iLmFoKCkKYi5hPWEuYQpiLmM9YS5jClAuSFooYixxKX1lbHNle3E9dC5GLmEoYi5jKQpiLmE9
-MgpiLmM9YQphLmpRKHEpfX0sCkhaOmZ1bmN0aW9uKGEsYTApe3ZhciBzLHIscSxwLG8sbixtLGwsayxq
-LGksaCxnLGYsZSxkLGM9e30sYj1jLmE9YQpmb3Iocz10Lm4scj10LkYscT10LmU7ITA7KXtwPXt9Cm89
-Yi5hPT09OAppZihhMD09bnVsbCl7aWYobyl7bj1zLmEoYi5jKQpQLlNpKG4uYSxuLmIpfXJldHVybn1w
-LmE9YTAKbT1hMC5hCmZvcihiPWEwO20hPW51bGw7Yj1tLG09bCl7Yi5hPW51bGwKUC5IWihjLmEsYikK
-cC5hPW0KbD1tLmF9az1jLmEKaj1rLmMKcC5iPW8KcC5jPWoKaT0hbwppZihpKXtoPWIuYwpoPShoJjEp
-IT09MHx8KGgmMTUpPT09OH1lbHNlIGg9ITAKaWYoaCl7Zz1iLmIuYgppZihvKXtrPWsuYj09PWcKaz0h
-KGt8fGspfWVsc2Ugaz0hMQppZihrKXtzLmEoaikKUC5TaShqLmEsai5iKQpyZXR1cm59Zj0kLlgzCmlm
-KGYhPT1nKSQuWDM9ZwplbHNlIGY9bnVsbApiPWIuYwppZigoYiYxNSk9PT04KW5ldyBQLlJUKHAsYyxv
-KS4kMCgpCmVsc2UgaWYoaSl7aWYoKGImMSkhPT0wKW5ldyBQLnJxKHAsaikuJDAoKX1lbHNlIGlmKChi
-JjIpIT09MCluZXcgUC5SVyhjLHApLiQwKCkKaWYoZiE9bnVsbCkkLlgzPWYKYj1wLmMKaWYocS5iKGIp
-KXtrPXAuYS4kdGkKaz1rLkMoImI4PDI+IikuYihiKXx8IWsuUVsxXS5iKGIpfWVsc2Ugaz0hMQppZihr
-KXtxLmEoYikKZT1wLmEuYgppZihiIGluc3RhbmNlb2YgUC52cylpZihiLmE+PTQpe2Q9ci5hKGUuYykK
-ZS5jPW51bGwKYTA9ZS5OOChkKQplLmE9Yi5hCmUuYz1iLmMKYy5hPWIKY29udGludWV9ZWxzZSBQLkE5
-KGIsZSkKZWxzZSBlLmVjKGIpCnJldHVybn19ZT1wLmEuYgpkPXIuYShlLmMpCmUuYz1udWxsCmEwPWUu
-TjgoZCkKYj1wLmIKaz1wLmMKaWYoIWIpe2UuJHRpLmMuYShrKQplLmE9NAplLmM9a31lbHNle3MuYShr
-KQplLmE9OAplLmM9a31jLmE9ZQpiPWV9fSwKVkg6ZnVuY3Rpb24oYSxiKXt2YXIgcwppZih0LmFnLmIo
-YSkpcmV0dXJuIGIuTGooYSx0LnosdC5LLHQubCkKcz10LmJJCmlmKHMuYihhKSlyZXR1cm4gcy5hKGEp
-CnRocm93IEguYihQLkwzKGEsIm9uRXJyb3IiLCJFcnJvciBoYW5kbGVyIG11c3QgYWNjZXB0IG9uZSBP
-YmplY3Qgb3Igb25lIE9iamVjdCBhbmQgYSBTdGFja1RyYWNlIGFzIGFyZ3VtZW50cywgYW5kIHJldHVy
-biBhIHZhbGlkIHJlc3VsdCIpKX0sCnB1OmZ1bmN0aW9uKCl7dmFyIHMscgpmb3Iocz0kLlM2O3MhPW51
-bGw7cz0kLlM2KXskLm1nPW51bGwKcj1zLmIKJC5TNj1yCmlmKHI9PW51bGwpJC5rOD1udWxsCnMuYS4k
-MCgpfX0sCmVOOmZ1bmN0aW9uKCl7JC5VRD0hMAp0cnl7UC5wdSgpfWZpbmFsbHl7JC5tZz1udWxsCiQu
-VUQ9ITEKaWYoJC5TNiE9bnVsbCkkLnV0KCkuJDEoUC5VSSgpKX19LAplVzpmdW5jdGlvbihhKXt2YXIg
-cz1uZXcgUC5PTShhKSxyPSQuazgKaWYocj09bnVsbCl7JC5TNj0kLms4PXMKaWYoISQuVUQpJC51dCgp
-LiQxKFAuVUkoKSl9ZWxzZSAkLms4PXIuYj1zfSwKclI6ZnVuY3Rpb24oYSl7dmFyIHMscixxLHA9JC5T
-NgppZihwPT1udWxsKXtQLmVXKGEpCiQubWc9JC5rOApyZXR1cm59cz1uZXcgUC5PTShhKQpyPSQubWcK
-aWYocj09bnVsbCl7cy5iPXAKJC5TNj0kLm1nPXN9ZWxzZXtxPXIuYgpzLmI9cQokLm1nPXIuYj1zCmlm
-KHE9PW51bGwpJC5rOD1zfX0sCnJiOmZ1bmN0aW9uKGEpe3ZhciBzPW51bGwscj0kLlgzCmlmKEMuTlU9
-PT1yKXtQLlRrKHMscyxDLk5VLGEpCnJldHVybn1QLlRrKHMscyxyLHQuTS5hKHIuR1koYSkpKX0sClF3
-OmZ1bmN0aW9uKGEsYil7SC5jYihhLCJzdHJlYW0iLHQuSykKcmV0dXJuIG5ldyBQLnhJKGIuQygieEk8
-MD4iKSl9LApTaTpmdW5jdGlvbihhLGIpe1AuclIobmV3IFAuRXYoYSxiKSl9LApUODpmdW5jdGlvbihh
-LGIsYyxkLGUpe3ZhciBzLHI9JC5YMwppZihyPT09YylyZXR1cm4gZC4kMCgpCiQuWDM9YwpzPXIKdHJ5
-e3I9ZC4kMCgpCnJldHVybiByfWZpbmFsbHl7JC5YMz1zfX0sCnl2OmZ1bmN0aW9uKGEsYixjLGQsZSxm
-LGcpe3ZhciBzLHI9JC5YMwppZihyPT09YylyZXR1cm4gZC4kMShlKQokLlgzPWMKcz1yCnRyeXtyPWQu
-JDEoZSkKcmV0dXJuIHJ9ZmluYWxseXskLlgzPXN9fSwKUXg6ZnVuY3Rpb24oYSxiLGMsZCxlLGYsZyxo
-LGkpe3ZhciBzLHI9JC5YMwppZihyPT09YylyZXR1cm4gZC4kMihlLGYpCiQuWDM9YwpzPXIKdHJ5e3I9
-ZC4kMihlLGYpCnJldHVybiByfWZpbmFsbHl7JC5YMz1zfX0sClRrOmZ1bmN0aW9uKGEsYixjLGQpe3Qu
-TS5hKGQpCmlmKEMuTlUhPT1jKWQ9Yy5HWShkKQpQLmVXKGQpfSwKdGg6ZnVuY3Rpb24gdGgoYSl7dGhp
-cy5hPWF9LApoYTpmdW5jdGlvbiBoYShhLGIsYyl7dGhpcy5hPWEKdGhpcy5iPWIKdGhpcy5jPWN9LApW
-czpmdW5jdGlvbiBWcyhhKXt0aGlzLmE9YX0sCkZ0OmZ1bmN0aW9uIEZ0KGEpe3RoaXMuYT1hfSwKVzM6
-ZnVuY3Rpb24gVzMoKXt9LAp5SDpmdW5jdGlvbiB5SChhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKaWg6
-ZnVuY3Rpb24gaWgoYSxiKXt0aGlzLmE9YQp0aGlzLmI9ITEKdGhpcy4kdGk9Yn0sCldNOmZ1bmN0aW9u
-IFdNKGEpe3RoaXMuYT1hfSwKU1g6ZnVuY3Rpb24gU1goYSl7dGhpcy5hPWF9LApHczpmdW5jdGlvbiBH
-cyhhKXt0aGlzLmE9YX0sCkZ5OmZ1bmN0aW9uIEZ5KGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9LApHVjpm
-dW5jdGlvbiBHVihhLGIpe3ZhciBfPXRoaXMKXy5hPWEKXy5kPV8uYz1fLmI9bnVsbApfLiR0aT1ifSwK
-cTQ6ZnVuY3Rpb24gcTQoYSxiKXt0aGlzLmE9YQp0aGlzLiR0aT1ifSwKQ3c6ZnVuY3Rpb24gQ3coYSxi
-KXt0aGlzLmE9YQp0aGlzLmI9Yn0sClBmOmZ1bmN0aW9uIFBmKCl7fSwKWmY6ZnVuY3Rpb24gWmYoYSxi
-KXt0aGlzLmE9YQp0aGlzLiR0aT1ifSwKRmU6ZnVuY3Rpb24gRmUoYSxiLGMsZCxlKXt2YXIgXz10aGlz
-Cl8uYT1udWxsCl8uYj1hCl8uYz1iCl8uZD1jCl8uZT1kCl8uJHRpPWV9LAp2czpmdW5jdGlvbiB2cyhh
-LGIpe3ZhciBfPXRoaXMKXy5hPTAKXy5iPWEKXy5jPW51bGwKXy4kdGk9Yn0sCmRhOmZ1bmN0aW9uIGRh
-KGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9LApvUTpmdW5jdGlvbiBvUShhLGIpe3RoaXMuYT1hCnRoaXMu
-Yj1ifSwKcFY6ZnVuY3Rpb24gcFYoYSl7dGhpcy5hPWF9LApVNzpmdW5jdGlvbiBVNyhhKXt0aGlzLmE9
-YX0sCnZyOmZ1bmN0aW9uIHZyKGEsYixjKXt0aGlzLmE9YQp0aGlzLmI9Ygp0aGlzLmM9Y30sCnJ0OmZ1
-bmN0aW9uIHJ0KGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9LApLRjpmdW5jdGlvbiBLRihhLGIpe3RoaXMu
-YT1hCnRoaXMuYj1ifSwKWkw6ZnVuY3Rpb24gWkwoYSxiLGMpe3RoaXMuYT1hCnRoaXMuYj1iCnRoaXMu
-Yz1jfSwKUlQ6ZnVuY3Rpb24gUlQoYSxiLGMpe3RoaXMuYT1hCnRoaXMuYj1iCnRoaXMuYz1jfSwKalo6
-ZnVuY3Rpb24galooYSl7dGhpcy5hPWF9LApycTpmdW5jdGlvbiBycShhLGIpe3RoaXMuYT1hCnRoaXMu
-Yj1ifSwKUlc6ZnVuY3Rpb24gUlcoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sCk9NOmZ1bmN0aW9uIE9N
-KGEpe3RoaXMuYT1hCnRoaXMuYj1udWxsfSwKcWg6ZnVuY3Rpb24gcWgoKXt9LApCNTpmdW5jdGlvbiBC
-NShhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKdU86ZnVuY3Rpb24gdU8oYSxiKXt0aGlzLmE9YQp0aGlz
-LmI9Yn0sCk1POmZ1bmN0aW9uIE1PKCl7fSwKa1Q6ZnVuY3Rpb24ga1QoKXt9LAp4STpmdW5jdGlvbiB4
-SShhKXt0aGlzLiR0aT1hfSwKbTA6ZnVuY3Rpb24gbTAoKXt9LApFdjpmdW5jdGlvbiBFdihhLGIpe3Ro
-aXMuYT1hCnRoaXMuYj1ifSwKSmk6ZnVuY3Rpb24gSmkoKXt9LApWcDpmdW5jdGlvbiBWcChhLGIpe3Ro
-aXMuYT1hCnRoaXMuYj1ifSwKT1I6ZnVuY3Rpb24gT1IoYSxiLGMpe3RoaXMuYT1hCnRoaXMuYj1iCnRo
-aXMuYz1jfSwKRUY6ZnVuY3Rpb24oYSxiLGMpe3JldHVybiBiLkMoIkA8MD4iKS5LcShjKS5DKCJGbzwx
-LDI+IikuYShILkI3KGEsbmV3IEguTjUoYi5DKCJAPDA+IikuS3EoYykuQygiTjU8MSwyPiIpKSkpfSwK
-Rmw6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gbmV3IEguTjUoYS5DKCJAPDA+IikuS3EoYikuQygiTjU8MSwy
-PiIpKX0sCkxzOmZ1bmN0aW9uKGEpe3JldHVybiBuZXcgUC5iNihhLkMoImI2PDA+IikpfSwKVDI6ZnVu
-Y3Rpb24oKXt2YXIgcz1PYmplY3QuY3JlYXRlKG51bGwpCnNbIjxub24taWRlbnRpZmllci1rZXk+Il09
-cwpkZWxldGUgc1siPG5vbi1pZGVudGlmaWVyLWtleT4iXQpyZXR1cm4gc30sCnJqOmZ1bmN0aW9uKGEs
-YixjKXt2YXIgcz1uZXcgUC5sbShhLGIsYy5DKCJsbTwwPiIpKQpzLmM9YS5lCnJldHVybiBzfSwKRVA6
-ZnVuY3Rpb24oYSxiLGMpe3ZhciBzLHIKaWYoUC5oQihhKSl7aWYoYj09PSIoIiYmYz09PSIpIilyZXR1
-cm4iKC4uLikiCnJldHVybiBiKyIuLi4iK2N9cz1ILlFJKFtdLHQucykKQy5ObS5pKCQuRixhKQp0cnl7
-UC5WcihhLHMpfWZpbmFsbHl7aWYoMD49JC5GLmxlbmd0aClyZXR1cm4gSC5PSCgkLkYsLTEpCiQuRi5w
-b3AoKX1yPVAubChiLHQudS5hKHMpLCIsICIpK2MKcmV0dXJuIHIuY2hhckNvZGVBdCgwKT09MD9yOnJ9
-LAp4OmZ1bmN0aW9uKGEsYixjKXt2YXIgcyxyCmlmKFAuaEIoYSkpcmV0dXJuIGIrIi4uLiIrYwpzPW5l
-dyBQLk0oYikKQy5ObS5pKCQuRixhKQp0cnl7cj1zCnIuYT1QLmwoci5hLGEsIiwgIil9ZmluYWxseXtp
-ZigwPj0kLkYubGVuZ3RoKXJldHVybiBILk9IKCQuRiwtMSkKJC5GLnBvcCgpfXMuYSs9YwpyPXMuYQpy
-ZXR1cm4gci5jaGFyQ29kZUF0KDApPT0wP3I6cn0sCmhCOmZ1bmN0aW9uKGEpe3ZhciBzLHIKZm9yKHM9
-JC5GLmxlbmd0aCxyPTA7cjxzOysrcilpZihhPT09JC5GW3JdKXJldHVybiEwCnJldHVybiExfSwKVnI6
-ZnVuY3Rpb24oYSxiKXt2YXIgcyxyLHEscCxvLG4sbSxsPWEuZ20oYSksaz0wLGo9MAp3aGlsZSghMCl7
-aWYoIShrPDgwfHxqPDMpKWJyZWFrCmlmKCFsLkYoKSlyZXR1cm4Kcz1ILkVqKGwuZ2woKSkKQy5ObS5p
-KGIscykKays9cy5sZW5ndGgrMjsrK2p9aWYoIWwuRigpKXtpZihqPD01KXJldHVybgppZigwPj1iLmxl
-bmd0aClyZXR1cm4gSC5PSChiLC0xKQpyPWIucG9wKCkKaWYoMD49Yi5sZW5ndGgpcmV0dXJuIEguT0go
-YiwtMSkKcT1iLnBvcCgpfWVsc2V7cD1sLmdsKCk7KytqCmlmKCFsLkYoKSl7aWYoajw9NCl7Qy5ObS5p
-KGIsSC5FaihwKSkKcmV0dXJufXI9SC5FaihwKQppZigwPj1iLmxlbmd0aClyZXR1cm4gSC5PSChiLC0x
-KQpxPWIucG9wKCkKays9ci5sZW5ndGgrMn1lbHNle289bC5nbCgpOysragpmb3IoO2wuRigpO3A9byxv
-PW4pe249bC5nbCgpOysragppZihqPjEwMCl7d2hpbGUoITApe2lmKCEoaz43NSYmaj4zKSlicmVhawpp
-ZigwPj1iLmxlbmd0aClyZXR1cm4gSC5PSChiLC0xKQprLT1iLnBvcCgpLmxlbmd0aCsyOy0tan1DLk5t
-LmkoYiwiLi4uIikKcmV0dXJufX1xPUguRWoocCkKcj1ILkVqKG8pCmsrPXIubGVuZ3RoK3EubGVuZ3Ro
-KzR9fWlmKGo+Yi5sZW5ndGgrMil7ays9NQptPSIuLi4ifWVsc2UgbT1udWxsCndoaWxlKCEwKXtpZigh
-KGs+ODAmJmIubGVuZ3RoPjMpKWJyZWFrCmlmKDA+PWIubGVuZ3RoKXJldHVybiBILk9IKGIsLTEpCmst
-PWIucG9wKCkubGVuZ3RoKzIKaWYobT09bnVsbCl7ays9NQptPSIuLi4ifX1pZihtIT1udWxsKUMuTm0u
-aShiLG0pCkMuTm0uaShiLHEpCkMuTm0uaShiLHIpfSwKdE06ZnVuY3Rpb24oYSxiKXt2YXIgcyxyLHE9
-UC5McyhiKQpmb3Iocz1hLmxlbmd0aCxyPTA7cjxhLmxlbmd0aDthLmxlbmd0aD09PXN8fCgwLEgubGsp
-KGEpLCsrcilxLmkoMCxiLmEoYVtyXSkpCnJldHVybiBxfSwKbk86ZnVuY3Rpb24oYSl7dmFyIHMscj17
-fQppZihQLmhCKGEpKXJldHVybiJ7Li4ufSIKcz1uZXcgUC5NKCIiKQp0cnl7Qy5ObS5pKCQuRixhKQpz
-LmErPSJ7IgpyLmE9ITAKYS5LKDAsbmV3IFAucmEocixzKSkKcy5hKz0ifSJ9ZmluYWxseXtpZigwPj0k
-LkYubGVuZ3RoKXJldHVybiBILk9IKCQuRiwtMSkKJC5GLnBvcCgpfXI9cy5hCnJldHVybiByLmNoYXJD
-b2RlQXQoMCk9PTA/cjpyfSwKYjY6ZnVuY3Rpb24gYjYoYSl7dmFyIF89dGhpcwpfLmE9MApfLmY9Xy5l
-PV8uZD1fLmM9Xy5iPW51bGwKXy5yPTAKXy4kdGk9YX0sCmJuOmZ1bmN0aW9uIGJuKGEpe3RoaXMuYT1h
-CnRoaXMuYz10aGlzLmI9bnVsbH0sCmxtOmZ1bmN0aW9uIGxtKGEsYixjKXt2YXIgXz10aGlzCl8uYT1h
-Cl8uYj1iCl8uZD1fLmM9bnVsbApfLiR0aT1jfSwKbVc6ZnVuY3Rpb24gbVcoKXt9LAp1eTpmdW5jdGlv
-biB1eSgpe30sCmxEOmZ1bmN0aW9uIGxEKCl7fSwKaWw6ZnVuY3Rpb24gaWwoKXt9LApyYTpmdW5jdGlv
-biByYShhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKWWs6ZnVuY3Rpb24gWWsoKXt9LAp5UTpmdW5jdGlv
-biB5UShhKXt0aGlzLmE9YX0sCktQOmZ1bmN0aW9uIEtQKCl7fSwKUG46ZnVuY3Rpb24gUG4oKXt9LApH
-ajpmdW5jdGlvbiBHaihhLGIpe3RoaXMuYT1hCnRoaXMuJHRpPWJ9LApsZjpmdW5jdGlvbiBsZigpe30s
-ClZqOmZ1bmN0aW9uIFZqKCl7fSwKWHY6ZnVuY3Rpb24gWHYoKXt9LApuWTpmdW5jdGlvbiBuWSgpe30s
-CldZOmZ1bmN0aW9uIFdZKCl7fSwKUlU6ZnVuY3Rpb24gUlUoKXt9LApwUjpmdW5jdGlvbiBwUigpe30s
-CkJTOmZ1bmN0aW9uKGEsYil7dmFyIHMscixxLHAKaWYodHlwZW9mIGEhPSJzdHJpbmciKXRocm93IEgu
-YihILnRMKGEpKQpzPW51bGwKdHJ5e3M9SlNPTi5wYXJzZShhKX1jYXRjaChxKXtyPUguUnUocSkKcD1Q
-LnJyKFN0cmluZyhyKSxudWxsLG51bGwpCnRocm93IEguYihwKX1wPVAuUWUocykKcmV0dXJuIHB9LApR
-ZTpmdW5jdGlvbihhKXt2YXIgcwppZihhPT1udWxsKXJldHVybiBudWxsCmlmKHR5cGVvZiBhIT0ib2Jq
-ZWN0IilyZXR1cm4gYQppZihPYmplY3QuZ2V0UHJvdG90eXBlT2YoYSkhPT1BcnJheS5wcm90b3R5cGUp
-cmV0dXJuIG5ldyBQLnV3KGEsT2JqZWN0LmNyZWF0ZShudWxsKSkKZm9yKHM9MDtzPGEubGVuZ3RoOysr
-cylhW3NdPVAuUWUoYVtzXSkKcmV0dXJuIGF9LApreTpmdW5jdGlvbihhLGIsYyxkKXt2YXIgcyxyCmlm
-KGIgaW5zdGFuY2VvZiBVaW50OEFycmF5KXtzPWIKZD1zLmxlbmd0aAppZihkLWM8MTUpcmV0dXJuIG51
-bGwKcj1QLlJQKGEscyxjLGQpCmlmKHIhPW51bGwmJmEpaWYoci5pbmRleE9mKCJcdWZmZmQiKT49MCly
-ZXR1cm4gbnVsbApyZXR1cm4gcn1yZXR1cm4gbnVsbH0sClJQOmZ1bmN0aW9uKGEsYixjLGQpe3ZhciBz
-PWE/JC5IRygpOiQucmYoKQppZihzPT1udWxsKXJldHVybiBudWxsCmlmKDA9PT1jJiZkPT09Yi5sZW5n
-dGgpcmV0dXJuIFAuUmIocyxiKQpyZXR1cm4gUC5SYihzLGIuc3ViYXJyYXkoYyxQLmpCKGMsZCxiLmxl
-bmd0aCkpKX0sClJiOmZ1bmN0aW9uKGEsYil7dmFyIHMscgp0cnl7cz1hLmRlY29kZShiKQpyZXR1cm4g
-c31jYXRjaChyKXtILlJ1KHIpfXJldHVybiBudWxsfSwKeE06ZnVuY3Rpb24oYSxiLGMsZCxlLGYpe2lm
-KEMuam4uelkoZiw0KSE9PTApdGhyb3cgSC5iKFAucnIoIkludmFsaWQgYmFzZTY0IHBhZGRpbmcsIHBh
-ZGRlZCBsZW5ndGggbXVzdCBiZSBtdWx0aXBsZSBvZiBmb3VyLCBpcyAiK2YsYSxjKSkKaWYoZCtlIT09
-Zil0aHJvdyBILmIoUC5ycigiSW52YWxpZCBiYXNlNjQgcGFkZGluZywgJz0nIG5vdCBhdCB0aGUgZW5k
-IixhLGIpKQppZihlPjIpdGhyb3cgSC5iKFAucnIoIkludmFsaWQgYmFzZTY0IHBhZGRpbmcsIG1vcmUg
-dGhhbiB0d28gJz0nIGNoYXJhY3RlcnMiLGEsYikpfSwKR3k6ZnVuY3Rpb24oYSxiLGMpe3JldHVybiBu
-ZXcgUC5VZChhLGIpfSwKTkM6ZnVuY3Rpb24oYSl7cmV0dXJuIGEuTHQoKX0sClVnOmZ1bmN0aW9uKGEs
-Yil7cmV0dXJuIG5ldyBQLnR1KGEsW10sUC5DeSgpKX0sCnVYOmZ1bmN0aW9uKGEsYixjKXt2YXIgcyxy
-PW5ldyBQLk0oIiIpLHE9UC5VZyhyLGIpCnEuaVUoYSkKcz1yLmEKcmV0dXJuIHMuY2hhckNvZGVBdCgw
-KT09MD9zOnN9LApqNDpmdW5jdGlvbihhKXtzd2l0Y2goYSl7Y2FzZSA2NTpyZXR1cm4iTWlzc2luZyBl
-eHRlbnNpb24gYnl0ZSIKY2FzZSA2NzpyZXR1cm4iVW5leHBlY3RlZCBleHRlbnNpb24gYnl0ZSIKY2Fz
-ZSA2OTpyZXR1cm4iSW52YWxpZCBVVEYtOCBieXRlIgpjYXNlIDcxOnJldHVybiJPdmVybG9uZyBlbmNv
-ZGluZyIKY2FzZSA3MzpyZXR1cm4iT3V0IG9mIHVuaWNvZGUgcmFuZ2UiCmNhc2UgNzU6cmV0dXJuIkVu
-Y29kZWQgc3Vycm9nYXRlIgpjYXNlIDc3OnJldHVybiJVbmZpbmlzaGVkIFVURi04IG9jdGV0IHNlcXVl
-bmNlIgpkZWZhdWx0OnJldHVybiIifX0sCmp5OmZ1bmN0aW9uKGEsYixjKXt2YXIgcyxyLHEscD1jLWIs
-bz1uZXcgVWludDhBcnJheShwKQpmb3Iocz1KLlU2KGEpLHI9MDtyPHA7KytyKXtxPXMucShhLGIrcikK
-aWYodHlwZW9mIHEhPT0ibnVtYmVyIilyZXR1cm4gcS56TSgpCmlmKChxJjQyOTQ5NjcwNDApPj4+MCE9
-PTApcT0yNTUKaWYocj49cClyZXR1cm4gSC5PSChvLHIpCm9bcl09cX1yZXR1cm4gb30sCnV3OmZ1bmN0
-aW9uIHV3KGEsYil7dGhpcy5hPWEKdGhpcy5iPWIKdGhpcy5jPW51bGx9LAppODpmdW5jdGlvbiBpOChh
-KXt0aGlzLmE9YX0sCnhyOmZ1bmN0aW9uIHhyKCl7fSwKTno6ZnVuY3Rpb24gTnooKXt9LApDVjpmdW5j
-dGlvbiBDVigpe30sClU4OmZ1bmN0aW9uIFU4KCl7fSwKVWs6ZnVuY3Rpb24gVWsoKXt9LAp3STpmdW5j
-dGlvbiB3SSgpe30sClppOmZ1bmN0aW9uIFppKCl7fSwKVWQ6ZnVuY3Rpb24gVWQoYSxiKXt0aGlzLmE9
-YQp0aGlzLmI9Yn0sCks4OmZ1bmN0aW9uIEs4KGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9LApieTpmdW5j
-dGlvbiBieSgpe30sCm9qOmZ1bmN0aW9uIG9qKGEpe3RoaXMuYj1hfSwKTXg6ZnVuY3Rpb24gTXgoYSl7
-dGhpcy5hPWF9LApTaDpmdW5jdGlvbiBTaCgpe30sCnRpOmZ1bmN0aW9uIHRpKGEsYil7dGhpcy5hPWEK
-dGhpcy5iPWJ9LAp0dTpmdW5jdGlvbiB0dShhLGIsYyl7dGhpcy5jPWEKdGhpcy5hPWIKdGhpcy5iPWN9
-LAp1NTpmdW5jdGlvbiB1NSgpe30sCkUzOmZ1bmN0aW9uIEUzKCl7fSwKUnc6ZnVuY3Rpb24gUncoYSl7
-dGhpcy5iPTAKdGhpcy5jPWF9LApHWTpmdW5jdGlvbiBHWShhKXt0aGlzLmE9YX0sCmJ6OmZ1bmN0aW9u
-IGJ6KGEpe3RoaXMuYT1hCnRoaXMuYj0xNgp0aGlzLmM9MH0sClFBOmZ1bmN0aW9uKGEsYil7dmFyIHM9
-SC5IcChhLGIpCmlmKHMhPW51bGwpcmV0dXJuIHMKdGhyb3cgSC5iKFAucnIoYSxudWxsLG51bGwpKX0s
-Cm9zOmZ1bmN0aW9uKGEpe2lmKGEgaW5zdGFuY2VvZiBILlRwKXJldHVybiBhLncoMCkKcmV0dXJuIklu
-c3RhbmNlIG9mICciK0guRWooSC5saChhKSkrIicifSwKTzg6ZnVuY3Rpb24oYSxiLGMsZCl7dmFyIHMs
-cj1jP0ouS2goYSxkKTpKLlFpKGEsZCkKaWYoYSE9PTAmJmIhPW51bGwpZm9yKHM9MDtzPHIubGVuZ3Ro
-OysrcylyW3NdPWIKcmV0dXJuIHJ9LApDSDpmdW5jdGlvbihhLGIsYyl7dmFyIHMscj1ILlFJKFtdLGMu
-QygiamQ8MD4iKSkKZm9yKHM9Si5JVChhKTtzLkYoKTspQy5ObS5pKHIsYy5hKHMuZ2woKSkpCmlmKGIp
-cmV0dXJuIHIKcmV0dXJuIEouRXAocixjKX0sClkxOmZ1bmN0aW9uKGEsYixjKXt2YXIgcwppZihiKXJl
-dHVybiBQLmV2KGEsYykKcz1KLkVwKFAuZXYoYSxjKSxjKQpyZXR1cm4gc30sCmV2OmZ1bmN0aW9uKGEs
-Yil7dmFyIHMscgppZihBcnJheS5pc0FycmF5KGEpKXJldHVybiBILlFJKGEuc2xpY2UoMCksYi5DKCJq
-ZDwwPiIpKQpzPUguUUkoW10sYi5DKCJqZDwwPiIpKQpmb3Iocj1KLklUKGEpO3IuRigpOylDLk5tLmko
-cyxyLmdsKCkpCnJldHVybiBzfSwKQUY6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gSi56QyhQLkNIKGEsITEs
-YikpfSwKSE06ZnVuY3Rpb24oYSxiLGMpe2lmKHQuYm0uYihhKSlyZXR1cm4gSC5mdyhhLGIsUC5qQihi
-LGMsYS5sZW5ndGgpKQpyZXR1cm4gUC5idyhhLGIsYyl9LApPbzpmdW5jdGlvbihhKXtyZXR1cm4gSC5M
-dyhhKX0sCmJ3OmZ1bmN0aW9uKGEsYixjKXt2YXIgcyxyLHEscCxvPW51bGwKaWYoYjwwKXRocm93IEgu
-YihQLlRFKGIsMCxhLmxlbmd0aCxvLG8pKQpzPWM9PW51bGwKaWYoIXMmJmM8Yil0aHJvdyBILmIoUC5U
-RShjLGIsYS5sZW5ndGgsbyxvKSkKcj1uZXcgSC5hNyhhLGEubGVuZ3RoLEgueksoYSkuQygiYTc8bEQu
-RT4iKSkKZm9yKHE9MDtxPGI7KytxKWlmKCFyLkYoKSl0aHJvdyBILmIoUC5URShiLDAscSxvLG8pKQpw
-PVtdCmlmKHMpZm9yKDtyLkYoKTspcC5wdXNoKHIuZCkKZWxzZSBmb3IocT1iO3E8YzsrK3Epe2lmKCFy
-LkYoKSl0aHJvdyBILmIoUC5URShjLGIscSxvLG8pKQpwLnB1c2goci5kKX1yZXR1cm4gSC5lVChwKX0s
-Cm51OmZ1bmN0aW9uKGEpe3JldHVybiBuZXcgSC5WUihhLEgudjQoYSwhMSwhMCwhMSwhMSwhMSkpfSwK
-bDpmdW5jdGlvbihhLGIsYyl7dmFyIHM9Si5JVChiKQppZighcy5GKCkpcmV0dXJuIGEKaWYoYy5sZW5n
-dGg9PT0wKXtkbyBhKz1ILkVqKHMuZ2woKSkKd2hpbGUocy5GKCkpfWVsc2V7YSs9SC5FaihzLmdsKCkp
-CmZvcig7cy5GKCk7KWE9YStjK0guRWoocy5nbCgpKX1yZXR1cm4gYX0sCmxyOmZ1bmN0aW9uKGEsYixj
-LGQpe3JldHVybiBuZXcgUC5tcChhLGIsYyxkKX0sCnVvOmZ1bmN0aW9uKCl7dmFyIHM9SC5NMCgpCmlm
-KHMhPW51bGwpcmV0dXJuIFAuaEsocykKdGhyb3cgSC5iKFAuTDQoIidVcmkuYmFzZScgaXMgbm90IHN1
-cHBvcnRlZCIpKX0sCmVQOmZ1bmN0aW9uKGEsYixjLGQpe3ZhciBzLHIscSxwLG8sbixtPSIwMTIzNDU2
-Nzg5QUJDREVGIgppZihjPT09Qy54TSl7cz0kLno0KCkuYgppZih0eXBlb2YgYiE9InN0cmluZyIpSC52
-KEgudEwoYikpCnM9cy50ZXN0KGIpfWVsc2Ugcz0hMQppZihzKXJldHVybiBiCkguTGgoYykuQygiVWsu
-UyIpLmEoYikKcj1jLmdaRSgpLldKKGIpCmZvcihzPXIubGVuZ3RoLHE9MCxwPSIiO3E8czsrK3Epe289
-cltxXQppZihvPDEyOCl7bj1vPj4+NAppZihuPj04KXJldHVybiBILk9IKGEsbikKbj0oYVtuXSYxPDwo
-byYxNSkpIT09MH1lbHNlIG49ITEKaWYobilwKz1ILkx3KG8pCmVsc2UgcD1kJiZvPT09MzI/cCsiKyI6
-cCsiJSIrbVtvPj4+NCYxNV0rbVtvJjE1XX1yZXR1cm4gcC5jaGFyQ29kZUF0KDApPT0wP3A6cH0sCkdx
-OmZ1bmN0aW9uKGEpe3ZhciBzPU1hdGguYWJzKGEpLHI9YTwwPyItIjoiIgppZihzPj0xMDAwKXJldHVy
-biIiK2EKaWYocz49MTAwKXJldHVybiByKyIwIitzCmlmKHM+PTEwKXJldHVybiByKyIwMCIrcwpyZXR1
-cm4gcisiMDAwIitzfSwKVng6ZnVuY3Rpb24oYSl7aWYoYT49MTAwKXJldHVybiIiK2EKaWYoYT49MTAp
-cmV0dXJuIjAiK2EKcmV0dXJuIjAwIithfSwKaDA6ZnVuY3Rpb24oYSl7aWYoYT49MTApcmV0dXJuIiIr
-YQpyZXR1cm4iMCIrYX0sCmhsOmZ1bmN0aW9uKGEpe2lmKHR5cGVvZiBhPT0ibnVtYmVyInx8SC5yUShh
-KXx8YT09bnVsbClyZXR1cm4gSi53KGEpCmlmKHR5cGVvZiBhPT0ic3RyaW5nIilyZXR1cm4gSlNPTi5z
-dHJpbmdpZnkoYSkKcmV0dXJuIFAub3MoYSl9LApoVjpmdW5jdGlvbihhKXtyZXR1cm4gbmV3IFAuQzYo
-YSl9LAp4WTpmdW5jdGlvbihhKXtyZXR1cm4gbmV3IFAuYyghMSxudWxsLG51bGwsYSl9LApMMzpmdW5j
-dGlvbihhLGIsYyl7cmV0dXJuIG5ldyBQLmMoITAsYSxiLGMpfSwKTzc6ZnVuY3Rpb24oYSxiKXtyZXR1
-cm4gbmV3IFAuYkoobnVsbCxudWxsLCEwLGEsYiwiVmFsdWUgbm90IGluIHJhbmdlIil9LApURTpmdW5j
-dGlvbihhLGIsYyxkLGUpe3JldHVybiBuZXcgUC5iSihiLGMsITAsYSxkLCJJbnZhbGlkIHZhbHVlIil9
-LAp3QTpmdW5jdGlvbihhLGIsYyxkKXtpZihhPGJ8fGE+Yyl0aHJvdyBILmIoUC5URShhLGIsYyxkLG51
-bGwpKQpyZXR1cm4gYX0sCmpCOmZ1bmN0aW9uKGEsYixjKXtpZigwPmF8fGE+Yyl0aHJvdyBILmIoUC5U
-RShhLDAsYywic3RhcnQiLG51bGwpKQppZihiIT1udWxsKXtpZihhPmJ8fGI+Yyl0aHJvdyBILmIoUC5U
-RShiLGEsYywiZW5kIixudWxsKSkKcmV0dXJuIGJ9cmV0dXJuIGN9LAprMTpmdW5jdGlvbihhLGIpe2lm
-KGE8MCl0aHJvdyBILmIoUC5URShhLDAsbnVsbCxiLG51bGwpKQpyZXR1cm4gYX0sCkNmOmZ1bmN0aW9u
-KGEsYixjLGQsZSl7dmFyIHM9SC51UChlPT1udWxsP0ouSG0oYik6ZSkKcmV0dXJuIG5ldyBQLmVZKHMs
-ITAsYSxjLCJJbmRleCBvdXQgb2YgcmFuZ2UiKX0sCkw0OmZ1bmN0aW9uKGEpe3JldHVybiBuZXcgUC51
-YihhKX0sClNZOmZ1bmN0aW9uKGEpe3JldHVybiBuZXcgUC5kcyhhKX0sClBWOmZ1bmN0aW9uKGEpe3Jl
-dHVybiBuZXcgUC5saihhKX0sCmE0OmZ1bmN0aW9uKGEpe3JldHVybiBuZXcgUC5VVihhKX0sCnJyOmZ1
-bmN0aW9uKGEsYixjKXtyZXR1cm4gbmV3IFAuYUUoYSxiLGMpfSwKaEs6ZnVuY3Rpb24oYTUpe3ZhciBz
-LHIscSxwLG8sbixtLGwsayxqLGksaCxnLGYsZSxkLGMsYixhLGEwLGExLGEyLGEzPW51bGwsYTQ9YTUu
-bGVuZ3RoCmlmKGE0Pj01KXtzPSgoSi5ReihhNSw0KV41OCkqM3xDLnhCLlcoYTUsMCleMTAwfEMueEIu
-VyhhNSwxKV45N3xDLnhCLlcoYTUsMileMTE2fEMueEIuVyhhNSwzKV45Nyk+Pj4wCmlmKHM9PT0wKXJl
-dHVybiBQLktEKGE0PGE0P0MueEIuTmooYTUsMCxhNCk6YTUsNSxhMykuZ2xSKCkKZWxzZSBpZihzPT09
-MzIpcmV0dXJuIFAuS0QoQy54Qi5OaihhNSw1LGE0KSwwLGEzKS5nbFIoKX1yPVAuTzgoOCwwLCExLHQu
-UykKQy5ObS5ZNShyLDAsMCkKQy5ObS5ZNShyLDEsLTEpCkMuTm0uWTUociwyLC0xKQpDLk5tLlk1KHIs
-NywtMSkKQy5ObS5ZNShyLDMsMCkKQy5ObS5ZNShyLDQsMCkKQy5ObS5ZNShyLDUsYTQpCkMuTm0uWTUo
-ciw2LGE0KQppZihQLlVCKGE1LDAsYTQsMCxyKT49MTQpQy5ObS5ZNShyLDcsYTQpCnE9clsxXQppZihx
-Pj0wKWlmKFAuVUIoYTUsMCxxLDIwLHIpPT09MjApcls3XT1xCnA9clsyXSsxCm89clszXQpuPXJbNF0K
-bT1yWzVdCmw9cls2XQppZihsPG0pbT1sCmlmKG48cCluPW0KZWxzZSBpZihuPD1xKW49cSsxCmlmKG88
-cClvPW4Kaz1yWzddPDAKaWYoaylpZihwPnErMyl7aj1hMwprPSExfWVsc2V7aT1vPjAKaWYoaSYmbysx
-PT09bil7aj1hMwprPSExfWVsc2V7aWYoIShtPGE0JiZtPT09bisyJiZKLnEwKGE1LCIuLiIsbikpKWg9
-bT5uKzImJkoucTAoYTUsIi8uLiIsbS0zKQplbHNlIGg9ITAKaWYoaCl7aj1hMwprPSExfWVsc2V7aWYo
-cT09PTQpaWYoSi5xMChhNSwiZmlsZSIsMCkpe2lmKHA8PTApe2lmKCFDLnhCLlFpKGE1LCIvIixuKSl7
-Zz0iZmlsZTovLy8iCnM9M31lbHNle2c9ImZpbGU6Ly8iCnM9Mn1hNT1nK0MueEIuTmooYTUsbixhNCkK
-cS09MAppPXMtMAptKz1pCmwrPWkKYTQ9YTUubGVuZ3RoCnA9NwpvPTcKbj03fWVsc2UgaWYobj09PW0p
-eysrbApmPW0rMQphNT1DLnhCLmk3KGE1LG4sbSwiLyIpOysrYTQKbT1mfWo9ImZpbGUifWVsc2UgaWYo
-Qy54Qi5RaShhNSwiaHR0cCIsMCkpe2lmKGkmJm8rMz09PW4mJkMueEIuUWkoYTUsIjgwIixvKzEpKXts
-LT0zCmU9bi0zCm0tPTMKYTU9Qy54Qi5pNyhhNSxvLG4sIiIpCmE0LT0zCm49ZX1qPSJodHRwIn1lbHNl
-IGo9YTMKZWxzZSBpZihxPT09NSYmSi5xMChhNSwiaHR0cHMiLDApKXtpZihpJiZvKzQ9PT1uJiZKLnEw
-KGE1LCI0NDMiLG8rMSkpe2wtPTQKZT1uLTQKbS09NAphNT1KLmRnKGE1LG8sbiwiIikKYTQtPTMKbj1l
-fWo9Imh0dHBzIn1lbHNlIGo9YTMKaz0hMH19fWVsc2Ugaj1hMwppZihrKXtpPWE1Lmxlbmd0aAppZihh
-NDxpKXthNT1KLmxkKGE1LDAsYTQpCnEtPTAKcC09MApvLT0wCm4tPTAKbS09MApsLT0wfXJldHVybiBu
-ZXcgUC5VZihhNSxxLHAsbyxuLG0sbCxqKX1pZihqPT1udWxsKWlmKHE+MClqPVAuUGkoYTUsMCxxKQpl
-bHNle2lmKHE9PT0wKXtQLlIzKGE1LDAsIkludmFsaWQgZW1wdHkgc2NoZW1lIikKSC5CaSh1LmcpfWo9
+Vy53eihzLHQuVSkKaWYoby5nQShvKT09PTApcmV0dXJuCkwucU8ocC5hKEMudDUuZ3RIKHMpKSl9fSwK
+YWY6ZnVuY3Rpb24oYSxiLGMsZCxlKXt2YXIgcyxyLHEscD10LkYsbz1MLkc2KHAuYSh3aW5kb3cubG9j
+YXRpb24pLmhyZWYpLG49TC5hSyhwLmEod2luZG93LmxvY2F0aW9uKS5ocmVmKQppZihvIT1udWxsKXtz
+PWRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJvIitILkVqKG8pKQppZihzIT1udWxsKUouZFIocykuUigw
+LCJ0YXJnZXQiKX1pZihuIT1udWxsKXtyPWRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoIi5saW5lLSIrSC5F
+aihuKSkKaWYociE9bnVsbCl7cT1yLnBhcmVudEVsZW1lbnQKcS50b1N0cmluZwpKLmRSKHEpLlIoMCwi
+aGlnaGxpZ2h0Iil9fWlmKGE9PXAuYSh3aW5kb3cubG9jYXRpb24pLnBhdGhuYW1lKXtMLmZHKGIsYykK
+ZS4kMCgpfWVsc2V7YS50b1N0cmluZwpMLkc3KGEsYixjLGQsZSl9fSwKUTQ6ZnVuY3Rpb24oYSxiKXt2
+YXIgcyxyLHE9UC5oSyhhKSxwPVAuRmwodC5OLHQuZGspCmZvcihzPXEuZ2hZKCkscz1zLmdQdShzKSxz
+PXMuZ20ocyk7cy5GKCk7KXtyPXMuZ2woKQpwLlk1KDAsci5hLHIuYil9Zm9yKHM9Yi5nUHUoYikscz1z
+LmdtKHMpO3MuRigpOyl7cj1zLmdsKCkKcC5ZNSgwLHIuYSxyLmIpfXAuWTUoMCwiYXV0aFRva2VuIiwk
+LlVFKCkpCnJldHVybiBxLm5tKDAscCkuZ25EKCl9LApUMTpmdW5jdGlvbihhKXt2YXIgcyxyLHEscCxv
+LG4sbSxsLGssaixpPSQuaEwoKQppLnRvU3RyaW5nCkoubDUoaSwiIikKaWYoYT09bnVsbCl7cz1kb2N1
+bWVudC5jcmVhdGVFbGVtZW50KCJwIikKQy5MdC5zYTQocywiU2VlIGRldGFpbHMgYWJvdXQgYSBwcm9w
+b3NlZCBlZGl0LiIpCkMuTHQuc1AocyxILlFJKFsicGxhY2Vob2xkZXIiXSx0LnMpKQppLmFwcGVuZENo
+aWxkKHMpCkMuTHQuRkYocykKcmV0dXJufXI9YS5kCnIudG9TdHJpbmcKcT0kLm5VKCkKcD1xLnpmKHIp
+Cm89YS5iCm49ZG9jdW1lbnQKbT1uLnF1ZXJ5U2VsZWN0b3IoIi5yb290IikudGV4dENvbnRlbnQKbS50
+b1N0cmluZwpsPXEuSFAocixDLnhCLmJTKG0pKQprPWEuYwpqPW4uY3JlYXRlRWxlbWVudCgicCIpCmku
+YXBwZW5kQ2hpbGQoaikKai5hcHBlbmRDaGlsZChuLmNyZWF0ZVRleHROb2RlKEguRWoobykrIiBhdCAi
+KSkKcj1hLmUKci50b1N0cmluZwpxPXQuTgpxPVcuSjYoTC5RNChyLFAuRUYoWyJsaW5lIixKLncoayld
+LHEscSkpKQpxLmFwcGVuZENoaWxkKG4uY3JlYXRlVGV4dE5vZGUobCsiOiIrSC5FaihrKSsiLiIpKQpq
+LmFwcGVuZENoaWxkKHEpCkouZGgoaikKTC5DQyhhLGkscCkKTC5GeihhLGkpfSwKTEg6ZnVuY3Rpb24o
+YSxiLGMpe3ZhciBzLHIscSxwLG8sbixtLGwsayxqLGksaCxnLGYsZSxkPSQueVAoKQpkLnRvU3RyaW5n
+CkoubDUoZCwiIikKaWYoYi5nQShiKT09PTApe3M9ZG9jdW1lbnQKcj1zLmNyZWF0ZUVsZW1lbnQoInAi
+KQpkLmFwcGVuZENoaWxkKHIpCnIuYXBwZW5kQ2hpbGQocy5jcmVhdGVUZXh0Tm9kZSgiTm8gcHJvcG9z
+ZWQgZWRpdHMiKSl9ZWxzZSBmb3IoZD1iLmdQdShiKSxkPWQuZ20oZCkscz10LkYscT10Lk4scD10LlEs
+bz1wLkMoIn4oMSk/Iiksbj10LloscD1wLmM7ZC5GKCk7KXttPWQuZ2woKQpsPWRvY3VtZW50CnI9bC5j
+cmVhdGVFbGVtZW50KCJwIikKaz0kLnlQKCkKay5hcHBlbmRDaGlsZChyKQpyLmFwcGVuZENoaWxkKGwu
+Y3JlYXRlVGV4dE5vZGUoSC5FaihtLmEpKyI6IikpCmo9bC5jcmVhdGVFbGVtZW50KCJ1bCIpCmsuYXBw
+ZW5kQ2hpbGQoaikKZm9yKG09Si5JVChtLmIpO20uRigpOyl7az1tLmdsKCkKaT1sLmNyZWF0ZUVsZW1l
+bnQoImxpIikKai5hcHBlbmRDaGlsZChpKQpKLmRSKGkpLmkoMCwiZWRpdCIpCmg9bC5jcmVhdGVFbGVt
+ZW50KCJhIikKaS5hcHBlbmRDaGlsZChoKQpoLmNsYXNzTGlzdC5hZGQoImVkaXQtbGluayIpCmc9ay5j
+CmY9SC5FaihnKQpoLnNldEF0dHJpYnV0ZSgiZGF0YS0iK25ldyBXLlN5KG5ldyBXLmk3KGgpKS5PVSgi
+b2Zmc2V0IiksZikKZT1rLmEKZj1ILkVqKGUpCmguc2V0QXR0cmlidXRlKCJkYXRhLSIrbmV3IFcuU3ko
+bmV3IFcuaTcoaCkpLk9VKCJsaW5lIiksZikKaC5hcHBlbmRDaGlsZChsLmNyZWF0ZVRleHROb2RlKCJs
+aW5lICIrSC5FaihlKSkpCmY9cy5hKHdpbmRvdy5sb2NhdGlvbikucGF0aG5hbWUKZi50b1N0cmluZwpo
+LnNldEF0dHJpYnV0ZSgiaHJlZiIsTC5RNChmLFAuRUYoWyJsaW5lIixILkVqKGUpLCJvZmZzZXQiLEgu
+RWooZyldLHEscSkpKQpmPW8uYShuZXcgTC5FRShnLGUsYSkpCm4uYShudWxsKQpXLkpFKGgsImNsaWNr
+IixmLCExLHApCmkuYXBwZW5kQ2hpbGQobC5jcmVhdGVUZXh0Tm9kZSgiOiAiK0guRWooay5iKSkpfX1p
+ZihjKUwuVDEobnVsbCl9LApGcjpmdW5jdGlvbihhLGIsYyl7dmFyIHMscixxPVAuaEsoQy5FeC5nRHIo
+dC5GLmEod2luZG93LmxvY2F0aW9uKSkrSC5FaihhKSkscD1QLkZsKHQuTix0LmRrKQppZihiIT1udWxs
+KXAuWTUoMCwib2Zmc2V0IixILkVqKGIpKQppZihjIT1udWxsKXAuWTUoMCwibGluZSIsSC5FaihjKSkK
+cC5ZNSgwLCJhdXRoVG9rZW4iLCQuVUUoKSkKcT1xLm5tKDAscCkKcD13aW5kb3cuaGlzdG9yeQpzPXQu
+egpyPXEuZ25EKCkKcC5wdXNoU3RhdGUobmV3IFAuQmYoW10sW10pLlB2KFAuRmwocyxzKSksIiIscil9
+LApFbjpmdW5jdGlvbihhKXt2YXIgcyxyPWRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoIi5yb290IikudGV4
+dENvbnRlbnQKci50b1N0cmluZwpzPXIrIi8iCmlmKEMueEIubihhLHMpKXJldHVybiBDLnhCLnluKGEs
+cy5sZW5ndGgpCmVsc2UgcmV0dXJuIGF9LApPdDpmdW5jdGlvbihhKXtzd2l0Y2goYS5yKXtjYXNlIEMu
+Y3c6YnJlYWsKY2FzZSBDLldEOmEucj1DLlhqCmJyZWFrCmNhc2UgQy5YajphLnI9Qy5XRApicmVhawpj
+YXNlIEMuZGM6dGhyb3cgSC5iKFAuUFYoIkZpbGUgIitILkVqKGEuYykrIiBzaG91bGQgbm90IGhhdmUg
+aW5kZXRlcm1pbmF0ZSBtaWdyYXRpb24gc3RhdHVzIikpCmRlZmF1bHQ6YnJlYWt9fSwKdGE6ZnVuY3Rp
+b24oYSxiKXt2YXIgcyxyPSJjaGVja19ib3giLHE9InRpdGxlIixwPSJvcHRlZC1vdXQiLG89Im1pZ3Jh
+dGluZyIKc3dpdGNoKGIpe2Nhc2UgQy5jdzphLmlubmVyVGV4dD1yCnM9Si5ZRShhKQpzLmdQKGEpLmko
+MCwiYWxyZWFkeS1taWdyYXRlZCIpCnMuZ1AoYSkuaSgwLCJkaXNhYmxlZCIpCmEuc2V0QXR0cmlidXRl
+KHEsIkFscmVhZHkgbWlncmF0ZWQiKQpicmVhawpjYXNlIEMuV0Q6YS5pbm5lclRleHQ9cgpzPUouWUUo
+YSkKcy5nUChhKS5SKDAscCkKcy5nUChhKS5pKDAsbykKYS5zZXRBdHRyaWJ1dGUocSwiTWlncmF0aW5n
+IHRvIG51bGwgc2FmZXR5IikKYnJlYWsKY2FzZSBDLlhqOmEuaW5uZXJUZXh0PSJjaGVja19ib3hfb3V0
+bGluZV9ibGFuayIKcz1KLllFKGEpCnMuZ1AoYSkuUigwLG8pCnMuZ1AoYSkuaSgwLHApCmEuc2V0QXR0
+cmlidXRlKHEsIk9wdGluZyBvdXQgb2YgbnVsbCBzYWZldHkiKQpicmVhawpkZWZhdWx0OmEuaW5uZXJU
+ZXh0PSJpbmRldGVybWluYXRlX2NoZWNrX2JveCIKcz1KLllFKGEpCnMuZ1AoYSkuUigwLG8pCnMuZ1Ao
+YSkuaSgwLHApCmEuc2V0QXR0cmlidXRlKHEsIk1peGVkIHN0YXR1c2VzIG9mICdtaWdyYXRpbmcnIGFu
+ZCAnb3B0aW5nIG91dCciKQpicmVha319LAp4bjpmdW5jdGlvbihhLGIpe3ZhciBzLHI9ImRpc2FibGVk
+IixxPWIuZ0woKQpMLnRhKGEscSkKaWYoYi5jPT09JC5EOSgpLmlubmVyVGV4dCl7aWYoYiBpbnN0YW5j
+ZW9mIEwuY0Qpe3M9Yi54CnMudG9TdHJpbmcKcz0hc31lbHNlIHM9ITEKaWYocyl7YS50b1N0cmluZwpK
+LmRSKGEpLmkoMCxyKX1lbHNle2EudG9TdHJpbmcKSi5kUihhKS5SKDAscil9TC50YSgkLmMwKCkscSl9
+fSwKQlg6ZnVuY3Rpb24oYSxiKXt2YXIgcyxyLHEscD17fQpwLmE9YQphPUwuRW4oYSkKcC5hPWEKcz0k
+LkQ5KCkKcy50b1N0cmluZwpKLmRyKHMsYSkKcz1kb2N1bWVudApyPXQuaApILkRoKHIsciwiVCIsInF1
+ZXJ5U2VsZWN0b3JBbGwiKQpzPW5ldyBXLnd6KHMucXVlcnlTZWxlY3RvckFsbCgiLm5hdi1wYW5lbCAu
+bmF2LWxpbmsiKSx0LlUpCnMuSyhzLG5ldyBMLlZTKHApKQpzPSQuSVIKcT1zPT1udWxsP251bGw6TC55
+dyhzLHAuYSkKaWYocT09bnVsbCl7cD0kLmJOKCkKcC50b1N0cmluZwpKLmRSKHApLlIoMCwidmlzaWJs
+ZSIpfWVsc2V7cD0kLmJOKCkKcC50b1N0cmluZwpKLmRSKHApLmkoMCwidmlzaWJsZSIpCkwudGEoJC5j
+MCgpLHEuZ0woKSl9fSwKQVI6ZnVuY3Rpb24oYSxiKXt2YXIgcyxyLHE9Yi5iCmlmKHE9PT0kKXE9SC52
+KG5ldyBILmMoIkZpZWxkICdwYXJlbnQnIGhhcyBub3QgYmVlbiBpbml0aWFsaXplZC4iKSkKaWYocSE9
+bnVsbCl7cz10LmgKcj1zLmEocy5hKGEucGFyZW50Tm9kZSkucGFyZW50Tm9kZSkKTC54bihyLnF1ZXJ5
+U2VsZWN0b3IoIjpzY29wZSA+IC5zdGF0dXMtaWNvbiIpLHEpCkwuQVIocixxKX19LApiTDpmdW5jdGlv
+bihhLGIpe3ZhciBzLHIscSxwLG8sbixtLGw9IjpzY29wZSA+IC5zdGF0dXMtaWNvbiIKZm9yKHM9Yi5k
+LHI9cy5sZW5ndGgscT10LmgscD0wO3A8cy5sZW5ndGg7cy5sZW5ndGg9PT1yfHwoMCxILmxrKShzKSwr
+K3Ape289c1twXQphLnRvU3RyaW5nCm49by5jCm4udG9TdHJpbmcKbT1hLnF1ZXJ5U2VsZWN0b3IoJ1tk
+YXRhLW5hbWUqPSInK1cuTGoobikrJyJdJykKaWYobyBpbnN0YW5jZW9mIEwudnQpe0wuYkwobSxvKQpM
+LnhuKG0ucXVlcnlTZWxlY3RvcihsKSxiKX1lbHNlIEwueG4ocS5hKG0ucGFyZW50Tm9kZSkucXVlcnlT
+ZWxlY3RvcihsKSxvKX19LApCRTpmdW5jdGlvbihhLGIsYyl7dmFyIHM9Ii5yZWdpb25zIixyPWRvY3Vt
+ZW50LHE9ci5xdWVyeVNlbGVjdG9yKHMpCnEudG9TdHJpbmcKcj1yLnF1ZXJ5U2VsZWN0b3IoIi5jb2Rl
+IikKci50b1N0cmluZwpKLnRIKHEsYi5hLCQuS0coKSkKSi50SChyLGIuYiwkLktHKCkpCkwuTEgoYSxi
+LmQsYykKaWYoYi5jLmxlbmd0aDwyZTUpTC52VSgpCkwueVgoIi5jb2RlIiwhMCkKTC55WChzLCEwKX0s
+CnRYOmZ1bmN0aW9uKGEwLGExLGEyKXt2YXIgcyxyLHEscCxvLG4sbSxsLGssaixpLGgsZyxmLGUsZD0i
+bWF0ZXJpYWwtaWNvbnMiLGM9InN0YXR1cy1pY29uIixiPWRvY3VtZW50LGE9Yi5jcmVhdGVFbGVtZW50
+KCJ1bCIpCmEwLmFwcGVuZENoaWxkKGEpCmZvcihzPWExLmxlbmd0aCxyPXQuTixxPXQuWixwPTA7cDxh
+MS5sZW5ndGg7YTEubGVuZ3RoPT09c3x8KDAsSC5saykoYTEpLCsrcCl7bz1hMVtwXQpuPWIuY3JlYXRl
+RWxlbWVudCgibGkiKQphLmFwcGVuZENoaWxkKG4pCmlmKG8gaW5zdGFuY2VvZiBMLnZ0KXttPUouWUUo
+bikKbS5nUChuKS5pKDAsImRpciIpCmw9by5jCmwudG9TdHJpbmcKbi5zZXRBdHRyaWJ1dGUoImRhdGEt
+IituZXcgVy5TeShuZXcgVy5pNyhuKSkuT1UoIm5hbWUiKSxsKQprPWIuY3JlYXRlRWxlbWVudCgic3Bh
+biIpCm4uYXBwZW5kQ2hpbGQoaykKbD1KLllFKGspCmwuZ1AoaykuaSgwLCJhcnJvdyIpCmwuc2hmKGss
+IiYjeDI1QkM7IikKaj1iLmNyZWF0ZUVsZW1lbnQoInNwYW4iKQpKLmRSKGopLmkoMCxkKQpqLmlubmVy
+VGV4dD0iZm9sZGVyX29wZW4iCm4uYXBwZW5kQ2hpbGQoaikKbD1vLmEKbC50b1N0cmluZwpuLmFwcGVu
+ZENoaWxkKGIuY3JlYXRlVGV4dE5vZGUobCkpCmw9by5kCmwudG9TdHJpbmcKTC50WChuLGwsITApCmk9
+Yi5jcmVhdGVFbGVtZW50KCJzcGFuIikKbD1KLllFKGkpCmwuZ1AoaSkuaSgwLGQpCmkuaW5uZXJUZXh0
+PSJpbmRldGVybWluYXRlX2NoZWNrX2JveCIKbC5nUChpKS5pKDAsYykKTC54bihpLG8pCmw9bC5nVmwo
+aSkKaD1sLiR0aQpnPWguQygifigxKT8iKS5hKG5ldyBMLlREKG8sbixpKSkKcS5hKG51bGwpClcuSkUo
+bC5hLGwuYixnLCExLGguYykKbS5tSyhuLGksaikKTC5reihrKX1lbHNlIGlmKG8gaW5zdGFuY2VvZiBM
+LmNEKXtpPWIuY3JlYXRlRWxlbWVudCgic3BhbiIpCm09Si5ZRShpKQptLmdQKGkpLmkoMCxkKQppLmlu
+bmVyVGV4dD0iIgptLmdQKGkpLmkoMCxjKQpsPW8ueApsLnRvU3RyaW5nCmg9IWwKaWYoaCltLmdQKGkp
+LmkoMCwiZGlzYWJsZWQiKQpMLnhuKGksbykKbC50b1N0cmluZwppZihsKXttPW0uZ1ZsKGkpCmw9bS4k
+dGkKaD1sLkMoIn4oMSk/IikuYShuZXcgTC5JZihvLGksbikpCnEuYShudWxsKQpXLkpFKG0uYSxtLmIs
+aCwhMSxsLmMpfW4uYXBwZW5kQ2hpbGQoaSkKbT1iLmNyZWF0ZUVsZW1lbnQoInNwYW4iKQpKLmRSKG0p
+LmkoMCxkKQptLmlubmVyVGV4dD0iaW5zZXJ0X2RyaXZlX2ZpbGUiCm4uYXBwZW5kQ2hpbGQobSkKZj1i
+LmNyZWF0ZUVsZW1lbnQoImEiKQpuLmFwcGVuZENoaWxkKGYpCm09Si5ZRShmKQptLmdQKGYpLmkoMCwi
+bmF2LWxpbmsiKQpsPW8uYwpsLnRvU3RyaW5nCmYuc2V0QXR0cmlidXRlKCJkYXRhLSIrbmV3IFcuU3ko
+bmV3IFcuaTcoZikpLk9VKCJuYW1lIiksbCkKbD1vLmQKbC50b1N0cmluZwpmLnNldEF0dHJpYnV0ZSgi
+aHJlZiIsTC5RNChsLFAuRmwocixyKSkpCmw9by5hCmwudG9TdHJpbmcKZi5hcHBlbmRDaGlsZChiLmNy
+ZWF0ZVRleHROb2RlKGwpKQptPW0uZ1ZsKGYpCmw9bS4kdGkKaD1sLkMoIn4oMSk/IikuYShuZXcgTC50
+QigpKQpxLmEobnVsbCkKVy5KRShtLmEsbS5iLGgsITEsbC5jKQpsPW8uZQpsLnRvU3RyaW5nCmlmKGw+
+MCl7ZT1iLmNyZWF0ZUVsZW1lbnQoInNwYW4iKQpuLmFwcGVuZENoaWxkKGUpCkouZFIoZSkuaSgwLCJl
+ZGl0LWNvdW50IikKbT0iIitsKyIgIgppZihsPT09MSloPSJwcm9wb3NlZCBlZGl0IgplbHNlIGg9InBy
+b3Bvc2VkIGVkaXRzIgplLnNldEF0dHJpYnV0ZSgidGl0bGUiLG0raCkKZS5hcHBlbmRDaGlsZChiLmNy
+ZWF0ZVRleHROb2RlKEMuam4udyhsKSkpfX19fSwKdXo6ZnVuY3Rpb24oYSxiLGMpe3ZhciBzPWRvY3Vt
+ZW50LHI9cy5jcmVhdGVFbGVtZW50KCJidXR0b24iKSxxPXQuUSxwPXEuQygifigxKT8iKS5hKG5ldyBM
+Lm0yKGEsYykpCnQuWi5hKG51bGwpClcuSkUociwiY2xpY2siLHAsITEscS5jKQpxPVIuT1goYS5hKQpx
+LnRvU3RyaW5nCnIuYXBwZW5kQ2hpbGQocy5jcmVhdGVUZXh0Tm9kZShxKSkKYi5hcHBlbmRDaGlsZChy
+KX0sCkZ6OmZ1bmN0aW9uKGEsYil7dmFyIHMscixxLHAsbyxuLG0sbCxrLGosaSxoPWEuYQppZihoPT1u
+dWxsKXJldHVybgpiLnRvU3RyaW5nCnM9ZG9jdW1lbnQKcj1zLmNyZWF0ZUVsZW1lbnQoInAiKQpxPWIu
+YXBwZW5kQ2hpbGQocikKcj1zLmNyZWF0ZUVsZW1lbnQoInNwYW4iKQpwPXQucwpKLk11KHIsSC5RSShb
+InR5cGUtZGVzY3JpcHRpb24iXSxwKSkKci5hcHBlbmRDaGlsZChzLmNyZWF0ZVRleHROb2RlKCJBY3Rp
+b25zIikpCnEuYXBwZW5kQ2hpbGQocikKcS5hcHBlbmRDaGlsZChzLmNyZWF0ZVRleHROb2RlKCI6Iikp
+Cm89cy5jcmVhdGVFbGVtZW50KCJwIikKYi5hcHBlbmRDaGlsZChvKQpmb3Iocj1oLmxlbmd0aCxuPXQu
+TyxtPTA7bTxoLmxlbmd0aDtoLmxlbmd0aD09PXJ8fCgwLEgubGspKGgpLCsrbSl7bD1oW21dCms9cy5j
+cmVhdGVFbGVtZW50KCJhIikKby5hcHBlbmRDaGlsZChrKQpqPWwuYQpqLnRvU3RyaW5nCmsuYXBwZW5k
+Q2hpbGQocy5jcmVhdGVUZXh0Tm9kZShqKSkKaj1sLmIKai50b1N0cmluZwprLnNldEF0dHJpYnV0ZSgi
+aHJlZiIsaikKaj1uLmEoSC5RSShbImFkZC1oaW50LWxpbmsiLCJiZWZvcmUtYXBwbHkiLCJidXR0b24i
+XSxwKSkKaT1KLmRSKGspCmkuVjEoMCkKaS5GVigwLGopfX0sCkNDOmZ1bmN0aW9uKGE0LGE1LGE2KXt2
+YXIgcyxyLHEscCxvLG4sbSxsLGssaixpLGgsZyxmLGUsZCxjLGIsYSxhMCxhMSxhMixhMwpmb3Iocz1h
+NC5mLHI9cy5sZW5ndGgscT10LnMscD10Lk8sbz0wO288cy5sZW5ndGg7cy5sZW5ndGg9PT1yfHwoMCxI
+LmxrKShzKSwrK28pe249c1tvXQphNS50b1N0cmluZwptPWRvY3VtZW50Cmw9bS5jcmVhdGVFbGVtZW50
+KCJwIikKaz1wLmEoSC5RSShbInRyYWNlIl0scSkpCmo9Si5kUihsKQpqLlYxKDApCmouRlYoMCxrKQpp
+PWE1LmFwcGVuZENoaWxkKGwpCmw9bS5jcmVhdGVFbGVtZW50KCJzcGFuIikKaz1wLmEoSC5RSShbInR5
+cGUtZGVzY3JpcHRpb24iXSxxKSkKaj1KLmRSKGwpCmouVjEoMCkKai5GVigwLGspCms9bi5hCmsudG9T
+dHJpbmcKbC5hcHBlbmRDaGlsZChtLmNyZWF0ZVRleHROb2RlKGspKQppLmFwcGVuZENoaWxkKGwpCmku
+YXBwZW5kQ2hpbGQobS5jcmVhdGVUZXh0Tm9kZSgiOiIpKQpsPW0uY3JlYXRlRWxlbWVudCgidWwiKQpr
+PXAuYShILlFJKFsidHJhY2UiXSxxKSkKaj1KLmRSKGwpCmouVjEoMCkKai5GVigwLGspCmg9aS5hcHBl
+bmRDaGlsZChsKQpmb3IobD1uLmIsaz1sLmxlbmd0aCxnPTA7ZzxsLmxlbmd0aDtsLmxlbmd0aD09PWt8
+fCgwLEgubGspKGwpLCsrZyl7Zj1sW2ddCmU9bS5jcmVhdGVFbGVtZW50KCJsaSIpCmguYXBwZW5kQ2hp
+bGQoZSkKZD1tLmNyZWF0ZUVsZW1lbnQoInNwYW4iKQpjPXAuYShILlFJKFsiZnVuY3Rpb24iXSxxKSkK
+aj1KLmRSKGQpCmouVjEoMCkKai5GVigwLGMpCmM9Zi5iCkwuV2ooZCxjPT1udWxsPyJ1bmtub3duIjpj
+KQplLmFwcGVuZENoaWxkKGQpCmI9Zi5jCmlmKGIhPW51bGwpe2UuYXBwZW5kQ2hpbGQobS5jcmVhdGVU
+ZXh0Tm9kZSgiICgiKSkKYT1iLmIKYTA9bS5jcmVhdGVFbGVtZW50KCJhIikKYTAuYXBwZW5kQ2hpbGQo
+bS5jcmVhdGVUZXh0Tm9kZShILkVqKGIuYykrIjoiK0guRWooYSkpKQpkPWIuYQpkLnRvU3RyaW5nCmEw
+LnNldEF0dHJpYnV0ZSgiaHJlZiIsZCkKYTAuY2xhc3NMaXN0LmFkZCgibmF2LWxpbmsiKQplLmFwcGVu
+ZENoaWxkKGEwKQplLmFwcGVuZENoaWxkKG0uY3JlYXRlVGV4dE5vZGUoIikiKSl9ZS5hcHBlbmRDaGls
+ZChtLmNyZWF0ZVRleHROb2RlKCI6ICIpKQpkPWYuYQpMLldqKGUsZD09bnVsbD8idW5rbm93biI6ZCkK
+ZD1mLmQKaWYoZC5sZW5ndGghPT0wKXtjPW0uY3JlYXRlRWxlbWVudCgicCIpCmExPXAuYShILlFJKFsi
+ZHJhd2VyIiwiYmVmb3JlLWFwcGx5Il0scSkpCmo9Si5kUihjKQpqLlYxKDApCmouRlYoMCxhMSkKYTI9
+ZS5hcHBlbmRDaGlsZChjKQpmb3IoYz1kLmxlbmd0aCxhMz0wO2EzPGQubGVuZ3RoO2QubGVuZ3RoPT09
+Y3x8KDAsSC5saykoZCksKythMylMLnV6KGRbYTNdLGEyLGIpfX19fSwKVXM6ZnVuY3Rpb24oYSl7cmV0
+dXJuIEMueEIudGcoYSwiPyIpP0MueEIuTmooYSwwLEMueEIuT1koYSwiPyIpKTphfSwKVEc6ZnVuY3Rp
+b24oYSxiKXtyZXR1cm4gbmV3IEwuUVcoYSxiKX0sCnl3OmZ1bmN0aW9uKGEsYil7dmFyIHMscixxLHAs
+bwpmb3Iocz1hLmxlbmd0aCxyPTA7cjxhLmxlbmd0aDthLmxlbmd0aD09PXN8fCgwLEgubGspKGEpLCsr
+cil7cT1hW3JdCmlmKHEgaW5zdGFuY2VvZiBMLnZ0KXtwPXEuZApwLnRvU3RyaW5nCm89TC55dyhwLGIp
+CmlmKG8hPW51bGwpcmV0dXJuIG99ZWxzZSBpZihxLmM9PT1iKXJldHVybiBxfXJldHVybiBudWxsfSwK
+V2o6ZnVuY3Rpb24oYSxiKXt2YXIgcyxyLHEscD1ILlFJKGIuc3BsaXQoIi4iKSx0LnMpLG89Qy5ObS5n
+dEgocCksbj1kb2N1bWVudAphLmFwcGVuZENoaWxkKG4uY3JlYXRlVGV4dE5vZGUobykpCmZvcihvPUgu
+cUMocCwxLG51bGwsdC5OKSxzPW8uJHRpLG89bmV3IEguYTcobyxvLmdBKG8pLHMuQygiYTc8YUwuRT4i
+KSkscz1zLkMoImFMLkUiKSxyPUouWUUoYSk7by5GKCk7KXtxPXMuYShvLmQpCnIubnooYSwiYmVmb3Jl
+ZW5kIiwiJiM4MjAzOy4iLG51bGwsbnVsbCkKYS5hcHBlbmRDaGlsZChuLmNyZWF0ZVRleHROb2RlKHEp
+KX19LAplOmZ1bmN0aW9uIGUoKXt9LApWVzpmdW5jdGlvbiBWVyhhLGIsYyl7dGhpcy5hPWEKdGhpcy5i
+PWIKdGhpcy5jPWN9LApvWjpmdW5jdGlvbiBvWigpe30sCmpyOmZ1bmN0aW9uIGpyKCl7fSwKcWw6ZnVu
+Y3Rpb24gcWwoKXt9LAp5ODpmdW5jdGlvbiB5OCgpe30sCkhpOmZ1bmN0aW9uIEhpKCl7fSwKQlQ6ZnVu
+Y3Rpb24gQlQoKXt9LApQWTpmdW5jdGlvbiBQWSgpe30sCkw6ZnVuY3Rpb24gTCgpe30sCld4OmZ1bmN0
+aW9uIFd4KGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9LApBTzpmdW5jdGlvbiBBTyhhKXt0aGlzLmE9YX0s
+CmROOmZ1bmN0aW9uIGROKGEpe3RoaXMuYT1hfSwKSG86ZnVuY3Rpb24gSG8oYSl7dGhpcy5hPWF9LAp4
+ejpmdW5jdGlvbiB4eihhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKSUM6ZnVuY3Rpb24gSUMoKXt9LApm
+QzpmdW5jdGlvbiBmQyhhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKVG06ZnVuY3Rpb24gVG0oKXt9LApu
+VDpmdW5jdGlvbiBuVChhLGIsYyl7dGhpcy5hPWEKdGhpcy5iPWIKdGhpcy5jPWN9LApOWTpmdW5jdGlv
+biBOWShhKXt0aGlzLmE9YX0sCnVlOmZ1bmN0aW9uIHVlKCl7fSwKR0g6ZnVuY3Rpb24gR0goKXt9LApF
+RTpmdW5jdGlvbiBFRShhLGIsYyl7dGhpcy5hPWEKdGhpcy5iPWIKdGhpcy5jPWN9LApRTDpmdW5jdGlv
+biBRTChhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKVlM6ZnVuY3Rpb24gVlMoYSl7dGhpcy5hPWF9LApU
+RDpmdW5jdGlvbiBURChhLGIsYyl7dGhpcy5hPWEKdGhpcy5iPWIKdGhpcy5jPWN9LApJZjpmdW5jdGlv
+biBJZihhLGIsYyl7dGhpcy5hPWEKdGhpcy5iPWIKdGhpcy5jPWN9LAp0QjpmdW5jdGlvbiB0Qigpe30s
+Cm0yOmZ1bmN0aW9uIG0yKGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9LApRVzpmdW5jdGlvbiBRVyhhLGIp
+e3RoaXMuYT1hCnRoaXMuYj1ifSwKWEE6ZnVuY3Rpb24gWEEoKXt9LApaczpmdW5jdGlvbihhKXt2YXIg
+cyxyLHE9Si5VNihhKQppZihMLnAyKEguayhxLnEoYSwidHlwZSIpKSk9PT1DLlkyKXtzPUguayhxLnEo
+YSwibmFtZSIpKQpyPUguayhxLnEoYSwicGF0aCIpKQpxPXEucShhLCJzdWJ0cmVlIikKcT1uZXcgTC52
+dChxPT1udWxsP251bGw6TC5tSyhxKSxzLHIpCnEuTFYoKQpyZXR1cm4gcX1lbHNle3M9SC5rKHEucShh
+LCJuYW1lIikpCnI9SC5rKHEucShhLCJwYXRoIikpCnJldHVybiBuZXcgTC5jRChILmsocS5xKGEsImhy
+ZWYiKSksSC5VYyhxLnEoYSwiZWRpdENvdW50IikpLEguTTQocS5xKGEsIndhc0V4cGxpY2l0bHlPcHRl
+ZE91dCIpKSxMLnZCKEguVWMocS5xKGEsIm1pZ3JhdGlvblN0YXR1cyIpKSksSC5NNChxLnEoYSwibWln
+cmF0aW9uU3RhdHVzQ2FuQmVDaGFuZ2VkIikpLHMscil9fSwKbUs6ZnVuY3Rpb24oYSl7dmFyIHMscj1I
+LlFJKFtdLHQuZmgpCmZvcihzPUouSVQodC5SLmEoYSkpO3MuRigpOylyLnB1c2goTC5acyhzLmdsKCkp
+KQpyZXR1cm4gcn0sClZEOmZ1bmN0aW9uKGEpe3ZhciBzLHIscT1ILlFJKFtdLHQuZCkKZm9yKHM9YS5s
+ZW5ndGgscj0wO3I8YS5sZW5ndGg7YS5sZW5ndGg9PT1zfHwoMCxILmxrKShhKSwrK3IpcS5wdXNoKGFb
+cl0uTHQoKSkKcmV0dXJuIHF9LAp2QjpmdW5jdGlvbihhKXtpZihhPT1udWxsKXJldHVybiBudWxsCmlm
+KGE+Pj4wIT09YXx8YT49NClyZXR1cm4gSC5PSChDLmwwLGEpCnJldHVybiBDLmwwW2FdfSwKcDI6ZnVu
+Y3Rpb24oYSl7c3dpdGNoKGEpe2Nhc2UiZGlyZWN0b3J5IjpyZXR1cm4gQy5ZMgpjYXNlImZpbGUiOnJl
+dHVybiBDLnJmCmRlZmF1bHQ6dGhyb3cgSC5iKFAuUFYoIlVucmVjb2duaXplZCBuYXZpZ2F0aW9uIHRy
+ZWUgbm9kZSB0eXBlOiAiK0guRWooYSkpKX19LAp2dDpmdW5jdGlvbiB2dChhLGIsYyl7dmFyIF89dGhp
+cwpfLmQ9YQpfLmE9YgpfLmI9JApfLmM9Y30sCmNEOmZ1bmN0aW9uIGNEKGEsYixjLGQsZSxmLGcpe3Zh
+ciBfPXRoaXMKXy5kPWEKXy5lPWIKXy5mPWMKXy5yPWQKXy54PWUKXy5hPWYKXy5iPSQKXy5jPWd9LApE
+ODpmdW5jdGlvbiBEOCgpe30sCk85OmZ1bmN0aW9uIE85KGEpe3RoaXMuYj1hfSwKR2I6ZnVuY3Rpb24g
+R2IoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sCklWOmZ1bmN0aW9uIElWKGEsYixjLGQpe3ZhciBfPXRo
+aXMKXy5kPWEKXy5lPWIKXy5mPWMKXy5yPWR9fSxNPXsKWUY6ZnVuY3Rpb24oYSxiKXt2YXIgcyxyLHEs
+cCxvLG4sbSxsCmZvcihzPWIubGVuZ3RoLHI9MTtyPHM7KytyKXtpZihiW3JdPT1udWxsfHxiW3ItMV0h
+PW51bGwpY29udGludWUKZm9yKDtzPj0xO3M9cSl7cT1zLTEKaWYoYltxXSE9bnVsbClicmVha31wPW5l
+dyBQLk0oIiIpCm89IiIrKGErIigiKQpwLmE9bwpuPUgudDYoYikKbT1uLkMoIm5IPDE+IikKbD1uZXcg
+SC5uSChiLDAscyxtKQpsLkhkKGIsMCxzLG4uYykKbT1vK25ldyBILmxKKGwsbS5DKCJxVShhTC5FKSIp
+LmEobmV3IE0uTm8oKSksbS5DKCJsSjxhTC5FLHFVPiIpKS5rKDAsIiwgIikKcC5hPW0KcC5hPW0rKCIp
+OiBwYXJ0ICIrKHItMSkrIiB3YXMgbnVsbCwgYnV0IHBhcnQgIityKyIgd2FzIG5vdC4iKQp0aHJvdyBI
+LmIoUC54WShwLncoMCkpKX19LApsSTpmdW5jdGlvbiBsSShhKXt0aGlzLmE9YX0sCnE3OmZ1bmN0aW9u
+IHE3KCl7fSwKTm86ZnVuY3Rpb24gTm8oKXt9fSxPPXsKUmg6ZnVuY3Rpb24oKXt2YXIgcyxyPW51bGwK
+aWYoUC51bygpLmdGaSgpIT09ImZpbGUiKXJldHVybiAkLkViKCkKcz1QLnVvKCkKaWYoIUMueEIuVGMo
+cy5nSWkocyksIi8iKSlyZXR1cm4gJC5FYigpCmlmKFAuS0wociwiYS9iIixyLHIscixyLHIpLnQ0KCk9
+PT0iYVxcYiIpcmV0dXJuICQuS2soKQpyZXR1cm4gJC5iRCgpfSwKekw6ZnVuY3Rpb24gekwoKXt9fSxQ
+PXsKT2o6ZnVuY3Rpb24oKXt2YXIgcyxyLHE9e30KaWYoc2VsZi5zY2hlZHVsZUltbWVkaWF0ZSE9bnVs
+bClyZXR1cm4gUC5FWCgpCmlmKHNlbGYuTXV0YXRpb25PYnNlcnZlciE9bnVsbCYmc2VsZi5kb2N1bWVu
+dCE9bnVsbCl7cz1zZWxmLmRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoImRpdiIpCnI9c2VsZi5kb2N1bWVu
+dC5jcmVhdGVFbGVtZW50KCJzcGFuIikKcS5hPW51bGwKbmV3IHNlbGYuTXV0YXRpb25PYnNlcnZlcihI
+LnRSKG5ldyBQLnRoKHEpLDEpKS5vYnNlcnZlKHMse2NoaWxkTGlzdDp0cnVlfSkKcmV0dXJuIG5ldyBQ
+LmhhKHEscyxyKX1lbHNlIGlmKHNlbGYuc2V0SW1tZWRpYXRlIT1udWxsKXJldHVybiBQLnl0KCkKcmV0
+dXJuIFAucVcoKX0sClpWOmZ1bmN0aW9uKGEpe3NlbGYuc2NoZWR1bGVJbW1lZGlhdGUoSC50UihuZXcg
+UC5Wcyh0Lk0uYShhKSksMCkpfSwKb0E6ZnVuY3Rpb24oYSl7c2VsZi5zZXRJbW1lZGlhdGUoSC50Uihu
+ZXcgUC5GdCh0Lk0uYShhKSksMCkpfSwKQno6ZnVuY3Rpb24oYSl7dC5NLmEoYSkKUC5RTigwLGEpfSwK
+UU46ZnVuY3Rpb24oYSxiKXt2YXIgcz1uZXcgUC5XMygpCnMuQ1koYSxiKQpyZXR1cm4gc30sCkZYOmZ1
+bmN0aW9uKGEpe3JldHVybiBuZXcgUC5paChuZXcgUC52cygkLlgzLGEuQygidnM8MD4iKSksYS5DKCJp
+aDwwPiIpKX0sCkRJOmZ1bmN0aW9uKGEsYil7YS4kMigwLG51bGwpCmIuYj0hMApyZXR1cm4gYi5hfSwK
+alE6ZnVuY3Rpb24oYSxiKXtQLkplKGEsYil9LAp5QzpmdW5jdGlvbihhLGIpe2IuYU0oMCxhKX0sCmYz
+OmZ1bmN0aW9uKGEsYil7Yi53MChILlJ1KGEpLEgudHMoYSkpfSwKSmU6ZnVuY3Rpb24oYSxiKXt2YXIg
+cyxyLHE9bmV3IFAuV00oYikscD1uZXcgUC5TWChiKQppZihhIGluc3RhbmNlb2YgUC52cylhLlFkKHEs
+cCx0LnopCmVsc2V7cz10LnoKaWYodC5pLmIoYSkpYS5TcShxLHAscykKZWxzZXtyPW5ldyBQLnZzKCQu
+WDMsdC5jKQpyLmE9NApyLmM9YQpyLlFkKHEscCxzKX19fSwKbHo6ZnVuY3Rpb24oYSl7dmFyIHM9ZnVu
+Y3Rpb24oYixjKXtyZXR1cm4gZnVuY3Rpb24oZCxlKXt3aGlsZSh0cnVlKXRyeXtiKGQsZSkKYnJlYWt9
+Y2F0Y2gocil7ZT1yCmQ9Y319fShhLDEpCnJldHVybiAkLlgzLkxqKG5ldyBQLkdzKHMpLHQuSCx0LlMs
+dC56KX0sCkdROmZ1bmN0aW9uKGEpe3JldHVybiBuZXcgUC5GeShhLDEpfSwKVGg6ZnVuY3Rpb24oKXty
+ZXR1cm4gQy53UX0sClltOmZ1bmN0aW9uKGEpe3JldHVybiBuZXcgUC5GeShhLDMpfSwKbDA6ZnVuY3Rp
+b24oYSxiKXtyZXR1cm4gbmV3IFAucTQoYSxiLkMoInE0PDA+IikpfSwKVGw6ZnVuY3Rpb24oYSxiKXt2
+YXIgcz1ILmNiKGEsImVycm9yIix0LkspCnJldHVybiBuZXcgUC5DdyhzLGI9PW51bGw/UC52MChhKTpi
+KX0sCnYwOmZ1bmN0aW9uKGEpe3ZhciBzCmlmKHQubS5iKGEpKXtzPWEuZ0lJKCkKaWYocyE9bnVsbCly
+ZXR1cm4gc31yZXR1cm4gQy5wZH0sCkE5OmZ1bmN0aW9uKGEsYil7dmFyIHMscixxCmZvcihzPXQuYzty
+PWEuYSxyPT09MjspYT1zLmEoYS5jKQppZihyPj00KXtxPWIuYWgoKQpiLmE9YS5hCmIuYz1hLmMKUC5I
+WihiLHEpfWVsc2V7cT10LmUuYShiLmMpCmIuYT0yCmIuYz1hCmEualEocSl9fSwKSFo6ZnVuY3Rpb24o
+YSxhMCl7dmFyIHMscixxLHAsbyxuLG0sbCxrLGosaSxoLGcsZixlLGQsYz17fSxiPWMuYT1hCmZvcihz
+PXQubixyPXQuZSxxPXQuaTshMDspe3A9e30Kbz1iLmE9PT04CmlmKGEwPT1udWxsKXtpZihvKXtuPXMu
+YShiLmMpClAuU2kobi5hLG4uYil9cmV0dXJufXAuYT1hMAptPWEwLmEKZm9yKGI9YTA7bSE9bnVsbDti
+PW0sbT1sKXtiLmE9bnVsbApQLkhaKGMuYSxiKQpwLmE9bQpsPW0uYX1rPWMuYQpqPWsuYwpwLmI9bwpw
+LmM9agppPSFvCmlmKGkpe2g9Yi5jCmg9KGgmMSkhPT0wfHwoaCYxNSk9PT04fWVsc2UgaD0hMAppZiho
+KXtnPWIuYi5iCmlmKG8pe2s9ay5iPT09ZwprPSEoa3x8ayl9ZWxzZSBrPSExCmlmKGspe3MuYShqKQpQ
+LlNpKGouYSxqLmIpCnJldHVybn1mPSQuWDMKaWYoZiE9PWcpJC5YMz1nCmVsc2UgZj1udWxsCmI9Yi5j
+CmlmKChiJjE1KT09PTgpbmV3IFAuUlQocCxjLG8pLiQwKCkKZWxzZSBpZihpKXtpZigoYiYxKSE9PTAp
+bmV3IFAucnEocCxqKS4kMCgpfWVsc2UgaWYoKGImMikhPT0wKW5ldyBQLlJXKGMscCkuJDAoKQppZihm
+IT1udWxsKSQuWDM9ZgpiPXAuYwppZihxLmIoYikpe2s9cC5hLiR0aQprPWsuQygiYjg8Mj4iKS5iKGIp
+fHwhay5RWzFdLmIoYil9ZWxzZSBrPSExCmlmKGspe3EuYShiKQplPXAuYS5iCmlmKGIuYT49NCl7ZD1y
+LmEoZS5jKQplLmM9bnVsbAphMD1lLk44KGQpCmUuYT1iLmEKZS5jPWIuYwpjLmE9Ygpjb250aW51ZX1l
+bHNlIFAuQTkoYixlKQpyZXR1cm59fWU9cC5hLmIKZD1yLmEoZS5jKQplLmM9bnVsbAphMD1lLk44KGQp
+CmI9cC5iCms9cC5jCmlmKCFiKXtlLiR0aS5jLmEoaykKZS5hPTQKZS5jPWt9ZWxzZXtzLmEoaykKZS5h
+PTgKZS5jPWt9Yy5hPWUKYj1lfX0sClZIOmZ1bmN0aW9uKGEsYil7dmFyIHMKaWYodC5hZy5iKGEpKXJl
+dHVybiBiLkxqKGEsdC56LHQuSyx0LmwpCnM9dC5iSQppZihzLmIoYSkpcmV0dXJuIHMuYShhKQp0aHJv
+dyBILmIoUC5MMyhhLCJvbkVycm9yIiwiRXJyb3IgaGFuZGxlciBtdXN0IGFjY2VwdCBvbmUgT2JqZWN0
+IG9yIG9uZSBPYmplY3QgYW5kIGEgU3RhY2tUcmFjZSBhcyBhcmd1bWVudHMsIGFuZCByZXR1cm4gYSB2
+YWxpZCByZXN1bHQiKSl9LApwdTpmdW5jdGlvbigpe3ZhciBzLHIKZm9yKHM9JC5TNjtzIT1udWxsO3M9
+JC5TNil7JC5tZz1udWxsCnI9cy5iCiQuUzY9cgppZihyPT1udWxsKSQuazg9bnVsbApzLmEuJDAoKX19
+LAplTjpmdW5jdGlvbigpeyQuVUQ9ITAKdHJ5e1AucHUoKX1maW5hbGx5eyQubWc9bnVsbAokLlVEPSEx
+CmlmKCQuUzYhPW51bGwpJC51dCgpLiQxKFAuVUkoKSl9fSwKZVc6ZnVuY3Rpb24oYSl7dmFyIHM9bmV3
+IFAuT00oYSkscj0kLms4CmlmKHI9PW51bGwpeyQuUzY9JC5rOD1zCmlmKCEkLlVEKSQudXQoKS4kMShQ
+LlVJKCkpfWVsc2UgJC5rOD1yLmI9c30sCnJSOmZ1bmN0aW9uKGEpe3ZhciBzLHIscSxwPSQuUzYKaWYo
+cD09bnVsbCl7UC5lVyhhKQokLm1nPSQuazgKcmV0dXJufXM9bmV3IFAuT00oYSkKcj0kLm1nCmlmKHI9
+PW51bGwpe3MuYj1wCiQuUzY9JC5tZz1zfWVsc2V7cT1yLmIKcy5iPXEKJC5tZz1yLmI9cwppZihxPT1u
+dWxsKSQuazg9c319LApyYjpmdW5jdGlvbihhKXt2YXIgcz1udWxsLHI9JC5YMwppZihDLk5VPT09cil7
+UC5UayhzLHMsQy5OVSxhKQpyZXR1cm59UC5UayhzLHMscix0Lk0uYShyLkdZKGEpKSl9LApRdzpmdW5j
+dGlvbihhLGIpe0guY2IoYSwic3RyZWFtIix0LkspCnJldHVybiBuZXcgUC54SShiLkMoInhJPDA+Iikp
+fSwKU2k6ZnVuY3Rpb24oYSxiKXtQLnJSKG5ldyBQLkV2KGEsYikpfSwKVDg6ZnVuY3Rpb24oYSxiLGMs
+ZCxlKXt2YXIgcyxyPSQuWDMKaWYocj09PWMpcmV0dXJuIGQuJDAoKQokLlgzPWMKcz1yCnRyeXtyPWQu
+JDAoKQpyZXR1cm4gcn1maW5hbGx5eyQuWDM9c319LAp5djpmdW5jdGlvbihhLGIsYyxkLGUsZixnKXt2
+YXIgcyxyPSQuWDMKaWYocj09PWMpcmV0dXJuIGQuJDEoZSkKJC5YMz1jCnM9cgp0cnl7cj1kLiQxKGUp
+CnJldHVybiByfWZpbmFsbHl7JC5YMz1zfX0sClF4OmZ1bmN0aW9uKGEsYixjLGQsZSxmLGcsaCxpKXt2
+YXIgcyxyPSQuWDMKaWYocj09PWMpcmV0dXJuIGQuJDIoZSxmKQokLlgzPWMKcz1yCnRyeXtyPWQuJDIo
+ZSxmKQpyZXR1cm4gcn1maW5hbGx5eyQuWDM9c319LApUazpmdW5jdGlvbihhLGIsYyxkKXt0Lk0uYShk
+KQppZihDLk5VIT09YylkPWMuR1koZCkKUC5lVyhkKX0sCnRoOmZ1bmN0aW9uIHRoKGEpe3RoaXMuYT1h
+fSwKaGE6ZnVuY3Rpb24gaGEoYSxiLGMpe3RoaXMuYT1hCnRoaXMuYj1iCnRoaXMuYz1jfSwKVnM6ZnVu
+Y3Rpb24gVnMoYSl7dGhpcy5hPWF9LApGdDpmdW5jdGlvbiBGdChhKXt0aGlzLmE9YX0sClczOmZ1bmN0
+aW9uIFczKCl7fSwKeUg6ZnVuY3Rpb24geUgoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sCmloOmZ1bmN0
+aW9uIGloKGEsYil7dGhpcy5hPWEKdGhpcy5iPSExCnRoaXMuJHRpPWJ9LApXTTpmdW5jdGlvbiBXTShh
+KXt0aGlzLmE9YX0sClNYOmZ1bmN0aW9uIFNYKGEpe3RoaXMuYT1hfSwKR3M6ZnVuY3Rpb24gR3MoYSl7
+dGhpcy5hPWF9LApGeTpmdW5jdGlvbiBGeShhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKR1Y6ZnVuY3Rp
+b24gR1YoYSxiKXt2YXIgXz10aGlzCl8uYT1hCl8uZD1fLmM9Xy5iPW51bGwKXy4kdGk9Yn0sCnE0OmZ1
+bmN0aW9uIHE0KGEsYil7dGhpcy5hPWEKdGhpcy4kdGk9Yn0sCkN3OmZ1bmN0aW9uIEN3KGEsYil7dGhp
+cy5hPWEKdGhpcy5iPWJ9LApQZjpmdW5jdGlvbiBQZigpe30sClpmOmZ1bmN0aW9uIFpmKGEsYil7dGhp
+cy5hPWEKdGhpcy4kdGk9Yn0sCkZlOmZ1bmN0aW9uIEZlKGEsYixjLGQsZSl7dmFyIF89dGhpcwpfLmE9
+bnVsbApfLmI9YQpfLmM9YgpfLmQ9YwpfLmU9ZApfLiR0aT1lfSwKdnM6ZnVuY3Rpb24gdnMoYSxiKXt2
+YXIgXz10aGlzCl8uYT0wCl8uYj1hCl8uYz1udWxsCl8uJHRpPWJ9LApkYTpmdW5jdGlvbiBkYShhLGIp
+e3RoaXMuYT1hCnRoaXMuYj1ifSwKb1E6ZnVuY3Rpb24gb1EoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0s
+CnBWOmZ1bmN0aW9uIHBWKGEpe3RoaXMuYT1hfSwKVTc6ZnVuY3Rpb24gVTcoYSl7dGhpcy5hPWF9LAp2
+cjpmdW5jdGlvbiB2cihhLGIsYyl7dGhpcy5hPWEKdGhpcy5iPWIKdGhpcy5jPWN9LApydDpmdW5jdGlv
+biBydChhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKS0Y6ZnVuY3Rpb24gS0YoYSxiKXt0aGlzLmE9YQp0
+aGlzLmI9Yn0sClpMOmZ1bmN0aW9uIFpMKGEsYixjKXt0aGlzLmE9YQp0aGlzLmI9Ygp0aGlzLmM9Y30s
+ClJUOmZ1bmN0aW9uIFJUKGEsYixjKXt0aGlzLmE9YQp0aGlzLmI9Ygp0aGlzLmM9Y30sCmpaOmZ1bmN0
+aW9uIGpaKGEpe3RoaXMuYT1hfSwKcnE6ZnVuY3Rpb24gcnEoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0s
+ClJXOmZ1bmN0aW9uIFJXKGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9LApPTTpmdW5jdGlvbiBPTShhKXt0
+aGlzLmE9YQp0aGlzLmI9bnVsbH0sCnFoOmZ1bmN0aW9uIHFoKCl7fSwKQjU6ZnVuY3Rpb24gQjUoYSxi
+KXt0aGlzLmE9YQp0aGlzLmI9Yn0sCnVPOmZ1bmN0aW9uIHVPKGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9
+LApNTzpmdW5jdGlvbiBNTygpe30sCmtUOmZ1bmN0aW9uIGtUKCl7fSwKeEk6ZnVuY3Rpb24geEkoYSl7
+dGhpcy4kdGk9YX0sCm0wOmZ1bmN0aW9uIG0wKCl7fSwKRXY6ZnVuY3Rpb24gRXYoYSxiKXt0aGlzLmE9
+YQp0aGlzLmI9Yn0sCkppOmZ1bmN0aW9uIEppKCl7fSwKVnA6ZnVuY3Rpb24gVnAoYSxiKXt0aGlzLmE9
+YQp0aGlzLmI9Yn0sCk9SOmZ1bmN0aW9uIE9SKGEsYixjKXt0aGlzLmE9YQp0aGlzLmI9Ygp0aGlzLmM9
+Y30sCkVGOmZ1bmN0aW9uKGEsYixjKXtyZXR1cm4gYi5DKCJAPDA+IikuS3EoYykuQygiRm88MSwyPiIp
+LmEoSC5CNyhhLG5ldyBILk41KGIuQygiQDwwPiIpLktxKGMpLkMoIk41PDEsMj4iKSkpKX0sCkZsOmZ1
+bmN0aW9uKGEsYil7cmV0dXJuIG5ldyBILk41KGEuQygiQDwwPiIpLktxKGIpLkMoIk41PDEsMj4iKSl9
+LApMczpmdW5jdGlvbihhKXtyZXR1cm4gbmV3IFAuYjYoYS5DKCJiNjwwPiIpKX0sClQyOmZ1bmN0aW9u
+KCl7dmFyIHM9T2JqZWN0LmNyZWF0ZShudWxsKQpzWyI8bm9uLWlkZW50aWZpZXIta2V5PiJdPXMKZGVs
+ZXRlIHNbIjxub24taWRlbnRpZmllci1rZXk+Il0KcmV0dXJuIHN9LApyajpmdW5jdGlvbihhLGIsYyl7
+dmFyIHM9bmV3IFAubG0oYSxiLGMuQygibG08MD4iKSkKcy5jPWEuZQpyZXR1cm4gc30sCkVQOmZ1bmN0
+aW9uKGEsYixjKXt2YXIgcyxyCmlmKFAuaEIoYSkpe2lmKGI9PT0iKCImJmM9PT0iKSIpcmV0dXJuIigu
+Li4pIgpyZXR1cm4gYisiLi4uIitjfXM9SC5RSShbXSx0LnMpCkMuTm0uaSgkLnhnLGEpCnRyeXtQLlZy
+KGEscyl9ZmluYWxseXtpZigwPj0kLnhnLmxlbmd0aClyZXR1cm4gSC5PSCgkLnhnLC0xKQokLnhnLnBv
+cCgpfXI9UC5sKGIsdC5SLmEocyksIiwgIikrYwpyZXR1cm4gci5jaGFyQ29kZUF0KDApPT0wP3I6cn0s
+Cng6ZnVuY3Rpb24oYSxiLGMpe3ZhciBzLHIKaWYoUC5oQihhKSlyZXR1cm4gYisiLi4uIitjCnM9bmV3
+IFAuTShiKQpDLk5tLmkoJC54ZyxhKQp0cnl7cj1zCnIuYT1QLmwoci5hLGEsIiwgIil9ZmluYWxseXtp
+ZigwPj0kLnhnLmxlbmd0aClyZXR1cm4gSC5PSCgkLnhnLC0xKQokLnhnLnBvcCgpfXMuYSs9YwpyPXMu
+YQpyZXR1cm4gci5jaGFyQ29kZUF0KDApPT0wP3I6cn0sCmhCOmZ1bmN0aW9uKGEpe3ZhciBzLHIKZm9y
+KHM9JC54Zy5sZW5ndGgscj0wO3I8czsrK3IpaWYoYT09PSQueGdbcl0pcmV0dXJuITAKcmV0dXJuITF9
+LApWcjpmdW5jdGlvbihhLGIpe3ZhciBzLHIscSxwLG8sbixtLGw9YS5nbShhKSxrPTAsaj0wCndoaWxl
+KCEwKXtpZighKGs8ODB8fGo8MykpYnJlYWsKaWYoIWwuRigpKXJldHVybgpzPUguRWoobC5nbCgpKQpD
+Lk5tLmkoYixzKQprKz1zLmxlbmd0aCsyOysran1pZighbC5GKCkpe2lmKGo8PTUpcmV0dXJuCmlmKDA+
+PWIubGVuZ3RoKXJldHVybiBILk9IKGIsLTEpCnI9Yi5wb3AoKQppZigwPj1iLmxlbmd0aClyZXR1cm4g
+SC5PSChiLC0xKQpxPWIucG9wKCl9ZWxzZXtwPWwuZ2woKTsrK2oKaWYoIWwuRigpKXtpZihqPD00KXtD
+Lk5tLmkoYixILkVqKHApKQpyZXR1cm59cj1ILkVqKHApCmlmKDA+PWIubGVuZ3RoKXJldHVybiBILk9I
+KGIsLTEpCnE9Yi5wb3AoKQprKz1yLmxlbmd0aCsyfWVsc2V7bz1sLmdsKCk7KytqCmZvcig7bC5GKCk7
+cD1vLG89bil7bj1sLmdsKCk7KytqCmlmKGo+MTAwKXt3aGlsZSghMCl7aWYoIShrPjc1JiZqPjMpKWJy
+ZWFrCmlmKDA+PWIubGVuZ3RoKXJldHVybiBILk9IKGIsLTEpCmstPWIucG9wKCkubGVuZ3RoKzI7LS1q
+fUMuTm0uaShiLCIuLi4iKQpyZXR1cm59fXE9SC5FaihwKQpyPUguRWoobykKays9ci5sZW5ndGgrcS5s
+ZW5ndGgrNH19aWYoaj5iLmxlbmd0aCsyKXtrKz01Cm09Ii4uLiJ9ZWxzZSBtPW51bGwKd2hpbGUoITAp
+e2lmKCEoaz44MCYmYi5sZW5ndGg+MykpYnJlYWsKaWYoMD49Yi5sZW5ndGgpcmV0dXJuIEguT0goYiwt
+MSkKay09Yi5wb3AoKS5sZW5ndGgrMgppZihtPT1udWxsKXtrKz01Cm09Ii4uLiJ9fWlmKG0hPW51bGwp
+Qy5ObS5pKGIsbSkKQy5ObS5pKGIscSkKQy5ObS5pKGIscil9LAp0TTpmdW5jdGlvbihhLGIpe3ZhciBz
+LHIscT1QLkxzKGIpCmZvcihzPWEubGVuZ3RoLHI9MDtyPGEubGVuZ3RoO2EubGVuZ3RoPT09c3x8KDAs
+SC5saykoYSksKytyKXEuaSgwLGIuYShhW3JdKSkKcmV0dXJuIHF9LApuTzpmdW5jdGlvbihhKXt2YXIg
+cyxyPXt9CmlmKFAuaEIoYSkpcmV0dXJuInsuLi59IgpzPW5ldyBQLk0oIiIpCnRyeXtDLk5tLmkoJC54
+ZyxhKQpzLmErPSJ7IgpyLmE9ITAKYS5LKDAsbmV3IFAucmEocixzKSkKcy5hKz0ifSJ9ZmluYWxseXtp
+ZigwPj0kLnhnLmxlbmd0aClyZXR1cm4gSC5PSCgkLnhnLC0xKQokLnhnLnBvcCgpfXI9cy5hCnJldHVy
+biByLmNoYXJDb2RlQXQoMCk9PTA/cjpyfSwKYjY6ZnVuY3Rpb24gYjYoYSl7dmFyIF89dGhpcwpfLmE9
+MApfLmY9Xy5lPV8uZD1fLmM9Xy5iPW51bGwKXy5yPTAKXy4kdGk9YX0sCmJuOmZ1bmN0aW9uIGJuKGEp
+e3RoaXMuYT1hCnRoaXMuYz10aGlzLmI9bnVsbH0sCmxtOmZ1bmN0aW9uIGxtKGEsYixjKXt2YXIgXz10
+aGlzCl8uYT1hCl8uYj1iCl8uZD1fLmM9bnVsbApfLiR0aT1jfSwKbVc6ZnVuY3Rpb24gbVcoKXt9LAp1
+eTpmdW5jdGlvbiB1eSgpe30sCmxEOmZ1bmN0aW9uIGxEKCl7fSwKaWw6ZnVuY3Rpb24gaWwoKXt9LApy
+YTpmdW5jdGlvbiByYShhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKWWs6ZnVuY3Rpb24gWWsoKXt9LAp5
+UTpmdW5jdGlvbiB5UShhKXt0aGlzLmE9YX0sCktQOmZ1bmN0aW9uIEtQKCl7fSwKUG46ZnVuY3Rpb24g
+UG4oKXt9LApHajpmdW5jdGlvbiBHaihhLGIpe3RoaXMuYT1hCnRoaXMuJHRpPWJ9LApsZjpmdW5jdGlv
+biBsZigpe30sClZqOmZ1bmN0aW9uIFZqKCl7fSwKWHY6ZnVuY3Rpb24gWHYoKXt9LApuWTpmdW5jdGlv
+biBuWSgpe30sCldZOmZ1bmN0aW9uIFdZKCl7fSwKUlU6ZnVuY3Rpb24gUlUoKXt9LApwUjpmdW5jdGlv
+biBwUigpe30sCkJTOmZ1bmN0aW9uKGEsYil7dmFyIHMscixxLHA9bnVsbAp0cnl7cD1KU09OLnBhcnNl
+KGEpfWNhdGNoKHIpe3M9SC5SdShyKQpxPVAucnIoU3RyaW5nKHMpLG51bGwsbnVsbCkKdGhyb3cgSC5i
+KHEpfXE9UC5RZShwKQpyZXR1cm4gcX0sClFlOmZ1bmN0aW9uKGEpe3ZhciBzCmlmKGE9PW51bGwpcmV0
+dXJuIG51bGwKaWYodHlwZW9mIGEhPSJvYmplY3QiKXJldHVybiBhCmlmKE9iamVjdC5nZXRQcm90b3R5
+cGVPZihhKSE9PUFycmF5LnByb3RvdHlwZSlyZXR1cm4gbmV3IFAudXcoYSxPYmplY3QuY3JlYXRlKG51
+bGwpKQpmb3Iocz0wO3M8YS5sZW5ndGg7KytzKWFbc109UC5RZShhW3NdKQpyZXR1cm4gYX0sCmt5OmZ1
+bmN0aW9uKGEsYixjLGQpe3ZhciBzLHIKaWYoYiBpbnN0YW5jZW9mIFVpbnQ4QXJyYXkpe3M9YgpkPXMu
+bGVuZ3RoCmlmKGQtYzwxNSlyZXR1cm4gbnVsbApyPVAuUlAoYSxzLGMsZCkKaWYociE9bnVsbCYmYSlp
+ZihyLmluZGV4T2YoIlx1ZmZmZCIpPj0wKXJldHVybiBudWxsCnJldHVybiByfXJldHVybiBudWxsfSwK
+UlA6ZnVuY3Rpb24oYSxiLGMsZCl7dmFyIHM9YT8kLkhHKCk6JC5yZigpCmlmKHM9PW51bGwpcmV0dXJu
+IG51bGwKaWYoMD09PWMmJmQ9PT1iLmxlbmd0aClyZXR1cm4gUC5SYihzLGIpCnJldHVybiBQLlJiKHMs
+Yi5zdWJhcnJheShjLFAuakIoYyxkLGIubGVuZ3RoKSkpfSwKUmI6ZnVuY3Rpb24oYSxiKXt2YXIgcyxy
+CnRyeXtzPWEuZGVjb2RlKGIpCnJldHVybiBzfWNhdGNoKHIpe0guUnUocil9cmV0dXJuIG51bGx9LAp4
+TTpmdW5jdGlvbihhLGIsYyxkLGUsZil7aWYoQy5qbi56WShmLDQpIT09MCl0aHJvdyBILmIoUC5ycigi
+SW52YWxpZCBiYXNlNjQgcGFkZGluZywgcGFkZGVkIGxlbmd0aCBtdXN0IGJlIG11bHRpcGxlIG9mIGZv
+dXIsIGlzICIrZixhLGMpKQppZihkK2UhPT1mKXRocm93IEguYihQLnJyKCJJbnZhbGlkIGJhc2U2NCBw
+YWRkaW5nLCAnPScgbm90IGF0IHRoZSBlbmQiLGEsYikpCmlmKGU+Mil0aHJvdyBILmIoUC5ycigiSW52
+YWxpZCBiYXNlNjQgcGFkZGluZywgbW9yZSB0aGFuIHR3byAnPScgY2hhcmFjdGVycyIsYSxiKSl9LApH
+eTpmdW5jdGlvbihhLGIsYyl7cmV0dXJuIG5ldyBQLlVkKGEsYil9LApOQzpmdW5jdGlvbihhKXtyZXR1
+cm4gYS5MdCgpfSwKVWc6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gbmV3IFAudHUoYSxbXSxQLkN5KCkpfSwK
+dVg6ZnVuY3Rpb24oYSxiLGMpe3ZhciBzLHI9bmV3IFAuTSgiIikscT1QLlVnKHIsYikKcS5pVShhKQpz
+PXIuYQpyZXR1cm4gcy5jaGFyQ29kZUF0KDApPT0wP3M6c30sCmo0OmZ1bmN0aW9uKGEpe3N3aXRjaChh
+KXtjYXNlIDY1OnJldHVybiJNaXNzaW5nIGV4dGVuc2lvbiBieXRlIgpjYXNlIDY3OnJldHVybiJVbmV4
+cGVjdGVkIGV4dGVuc2lvbiBieXRlIgpjYXNlIDY5OnJldHVybiJJbnZhbGlkIFVURi04IGJ5dGUiCmNh
+c2UgNzE6cmV0dXJuIk92ZXJsb25nIGVuY29kaW5nIgpjYXNlIDczOnJldHVybiJPdXQgb2YgdW5pY29k
+ZSByYW5nZSIKY2FzZSA3NTpyZXR1cm4iRW5jb2RlZCBzdXJyb2dhdGUiCmNhc2UgNzc6cmV0dXJuIlVu
+ZmluaXNoZWQgVVRGLTggb2N0ZXQgc2VxdWVuY2UiCmRlZmF1bHQ6cmV0dXJuIiJ9fSwKank6ZnVuY3Rp
+b24oYSxiLGMpe3ZhciBzLHIscSxwPWMtYixvPW5ldyBVaW50OEFycmF5KHApCmZvcihzPUouVTYoYSks
+cj0wO3I8cDsrK3Ipe3E9cy5xKGEsYityKQppZigocSY0Mjk0OTY3MDQwKT4+PjAhPT0wKXE9MjU1Cmlm
+KHI+PXApcmV0dXJuIEguT0gobyxyKQpvW3JdPXF9cmV0dXJuIG99LAp1dzpmdW5jdGlvbiB1dyhhLGIp
+e3RoaXMuYT1hCnRoaXMuYj1iCnRoaXMuYz1udWxsfSwKaTg6ZnVuY3Rpb24gaTgoYSl7dGhpcy5hPWF9
+LAp4cjpmdW5jdGlvbiB4cigpe30sCk56OmZ1bmN0aW9uIE56KCl7fSwKQ1Y6ZnVuY3Rpb24gQ1YoKXt9
+LApVODpmdW5jdGlvbiBVOCgpe30sClVrOmZ1bmN0aW9uIFVrKCl7fSwKd0k6ZnVuY3Rpb24gd0koKXt9
+LApaaTpmdW5jdGlvbiBaaSgpe30sClVkOmZ1bmN0aW9uIFVkKGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9
+LApLODpmdW5jdGlvbiBLOChhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKYnk6ZnVuY3Rpb24gYnkoKXt9
+LApvajpmdW5jdGlvbiBvaihhKXt0aGlzLmI9YX0sCk14OmZ1bmN0aW9uIE14KGEpe3RoaXMuYT1hfSwK
+U2g6ZnVuY3Rpb24gU2goKXt9LAp0aTpmdW5jdGlvbiB0aShhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwK
+dHU6ZnVuY3Rpb24gdHUoYSxiLGMpe3RoaXMuYz1hCnRoaXMuYT1iCnRoaXMuYj1jfSwKdTU6ZnVuY3Rp
+b24gdTUoKXt9LApFMzpmdW5jdGlvbiBFMygpe30sClJ3OmZ1bmN0aW9uIFJ3KGEpe3RoaXMuYj0wCnRo
+aXMuYz1hfSwKR1k6ZnVuY3Rpb24gR1koYSl7dGhpcy5hPWF9LApiejpmdW5jdGlvbiBieihhKXt0aGlz
+LmE9YQp0aGlzLmI9MTYKdGhpcy5jPTB9LApRQTpmdW5jdGlvbihhLGIpe3ZhciBzPUguSHAoYSxiKQpp
+ZihzIT1udWxsKXJldHVybiBzCnRocm93IEguYihQLnJyKGEsbnVsbCxudWxsKSl9LApvczpmdW5jdGlv
+bihhKXtpZihhIGluc3RhbmNlb2YgSC5UcClyZXR1cm4gYS53KDApCnJldHVybiJJbnN0YW5jZSBvZiAn
+IitILmxoKGEpKyInIn0sCk84OmZ1bmN0aW9uKGEsYixjLGQpe3ZhciBzLHI9Yz9KLktoKGEsZCk6Si5R
+aShhLGQpCmlmKGEhPT0wJiZiIT1udWxsKWZvcihzPTA7czxyLmxlbmd0aDsrK3MpcltzXT1iCnJldHVy
+biByfSwKQ0g6ZnVuY3Rpb24oYSxiLGMpe3ZhciBzLHI9SC5RSShbXSxjLkMoImpkPDA+IikpCmZvcihz
+PUouSVQoYSk7cy5GKCk7KUMuTm0uaShyLGMuYShzLmdsKCkpKQppZihiKXJldHVybiByCnJldHVybiBK
+LkVwKHIsYyl9LApZMTpmdW5jdGlvbihhLGIsYyl7dmFyIHMKaWYoYilyZXR1cm4gUC5ldihhLGMpCnM9
+Si5FcChQLmV2KGEsYyksYykKcmV0dXJuIHN9LApldjpmdW5jdGlvbihhLGIpe3ZhciBzLHIKaWYoQXJy
+YXkuaXNBcnJheShhKSlyZXR1cm4gSC5RSShhLnNsaWNlKDApLGIuQygiamQ8MD4iKSkKcz1ILlFJKFtd
+LGIuQygiamQ8MD4iKSkKZm9yKHI9Si5JVChhKTtyLkYoKTspQy5ObS5pKHMsci5nbCgpKQpyZXR1cm4g
+c30sCkFGOmZ1bmN0aW9uKGEsYil7cmV0dXJuIEouekMoUC5DSChhLCExLGIpKX0sCkhNOmZ1bmN0aW9u
+KGEsYixjKXt2YXIgcz1ILmZ3KGEsYixQLmpCKGIsYyxhLmxlbmd0aCkpCnJldHVybiBzfSwKT286ZnVu
+Y3Rpb24oYSl7cmV0dXJuIEguTHcoYSl9LApudTpmdW5jdGlvbihhKXtyZXR1cm4gbmV3IEguVlIoYSxI
+LnY0KGEsITEsITAsITEsITEsITEpKX0sCmw6ZnVuY3Rpb24oYSxiLGMpe3ZhciBzPUouSVQoYikKaWYo
+IXMuRigpKXJldHVybiBhCmlmKGMubGVuZ3RoPT09MCl7ZG8gYSs9SC5FaihzLmdsKCkpCndoaWxlKHMu
+RigpKX1lbHNle2ErPUguRWoocy5nbCgpKQpmb3IoO3MuRigpOylhPWErYytILkVqKHMuZ2woKSl9cmV0
+dXJuIGF9LApscjpmdW5jdGlvbihhLGIsYyxkKXtyZXR1cm4gbmV3IFAubXAoYSxiLGMsZCl9LAp1bzpm
+dW5jdGlvbigpe3ZhciBzPUguTTAoKQppZihzIT1udWxsKXJldHVybiBQLmhLKHMpCnRocm93IEguYihQ
+Lkw0KCInVXJpLmJhc2UnIGlzIG5vdCBzdXBwb3J0ZWQiKSl9LAplUDpmdW5jdGlvbihhLGIsYyxkKXt2
+YXIgcyxyLHEscCxvLG4sbT0iMDEyMzQ1Njc4OUFCQ0RFRiIKaWYoYz09PUMueE0pe3M9JC56NCgpLmIK
+cz1zLnRlc3QoYil9ZWxzZSBzPSExCmlmKHMpcmV0dXJuIGIKSC5MaChjKS5DKCJVay5TIikuYShiKQpy
+PWMuZ1pFKCkuV0ooYikKZm9yKHM9ci5sZW5ndGgscT0wLHA9IiI7cTxzOysrcSl7bz1yW3FdCmlmKG88
+MTI4KXtuPW8+Pj40CmlmKG4+PTgpcmV0dXJuIEguT0goYSxuKQpuPShhW25dJjE8PChvJjE1KSkhPT0w
+fWVsc2Ugbj0hMQppZihuKXArPUguTHcobykKZWxzZSBwPWQmJm89PT0zMj9wKyIrIjpwKyIlIittW28+
+Pj40JjE1XSttW28mMTVdfXJldHVybiBwLmNoYXJDb2RlQXQoMCk9PTA/cDpwfSwKR3E6ZnVuY3Rpb24o
+YSl7dmFyIHM9TWF0aC5hYnMoYSkscj1hPDA/Ii0iOiIiCmlmKHM+PTEwMDApcmV0dXJuIiIrYQppZihz
+Pj0xMDApcmV0dXJuIHIrIjAiK3MKaWYocz49MTApcmV0dXJuIHIrIjAwIitzCnJldHVybiByKyIwMDAi
+K3N9LApWeDpmdW5jdGlvbihhKXtpZihhPj0xMDApcmV0dXJuIiIrYQppZihhPj0xMClyZXR1cm4iMCIr
+YQpyZXR1cm4iMDAiK2F9LApoMDpmdW5jdGlvbihhKXtpZihhPj0xMClyZXR1cm4iIithCnJldHVybiIw
+IithfSwKaGw6ZnVuY3Rpb24oYSl7aWYodHlwZW9mIGE9PSJudW1iZXIifHxILnJRKGEpfHxhPT1udWxs
+KXJldHVybiBKLncoYSkKaWYodHlwZW9mIGE9PSJzdHJpbmciKXJldHVybiBKU09OLnN0cmluZ2lmeShh
+KQpyZXR1cm4gUC5vcyhhKX0sCmhWOmZ1bmN0aW9uKGEpe3JldHVybiBuZXcgUC5DNihhKX0sCnhZOmZ1
+bmN0aW9uKGEpe3JldHVybiBuZXcgUC5BVCghMSxudWxsLG51bGwsYSl9LApMMzpmdW5jdGlvbihhLGIs
+Yyl7cmV0dXJuIG5ldyBQLkFUKCEwLGEsYixjKX0sCk83OmZ1bmN0aW9uKGEsYil7cmV0dXJuIG5ldyBQ
+LmJKKG51bGwsbnVsbCwhMCxhLGIsIlZhbHVlIG5vdCBpbiByYW5nZSIpfSwKVEU6ZnVuY3Rpb24oYSxi
+LGMsZCxlKXtyZXR1cm4gbmV3IFAuYkooYixjLCEwLGEsZCwiSW52YWxpZCB2YWx1ZSIpfSwKd0E6ZnVu
+Y3Rpb24oYSxiLGMsZCl7aWYoYTxifHxhPmMpdGhyb3cgSC5iKFAuVEUoYSxiLGMsZCxudWxsKSkKcmV0
+dXJuIGF9LApqQjpmdW5jdGlvbihhLGIsYyl7aWYoMD5hfHxhPmMpdGhyb3cgSC5iKFAuVEUoYSwwLGMs
+InN0YXJ0IixudWxsKSkKaWYoYiE9bnVsbCl7aWYoYT5ifHxiPmMpdGhyb3cgSC5iKFAuVEUoYixhLGMs
+ImVuZCIsbnVsbCkpCnJldHVybiBifXJldHVybiBjfSwKazE6ZnVuY3Rpb24oYSxiKXtpZihhPDApdGhy
+b3cgSC5iKFAuVEUoYSwwLG51bGwsYixudWxsKSkKcmV0dXJuIGF9LApDZjpmdW5jdGlvbihhLGIsYyxk
+LGUpe3ZhciBzPUguSVooZT09bnVsbD9KLkhtKGIpOmUpCnJldHVybiBuZXcgUC5lWShzLCEwLGEsYywi
+SW5kZXggb3V0IG9mIHJhbmdlIil9LApMNDpmdW5jdGlvbihhKXtyZXR1cm4gbmV3IFAudWIoYSl9LApT
+WTpmdW5jdGlvbihhKXtyZXR1cm4gbmV3IFAuZHMoYSl9LApQVjpmdW5jdGlvbihhKXtyZXR1cm4gbmV3
+IFAubGooYSl9LAphNDpmdW5jdGlvbihhKXtyZXR1cm4gbmV3IFAuVVYoYSl9LApycjpmdW5jdGlvbihh
+LGIsYyl7cmV0dXJuIG5ldyBQLmFFKGEsYixjKX0sCmhLOmZ1bmN0aW9uKGE1KXt2YXIgcyxyLHEscCxv
+LG4sbSxsLGssaixpLGgsZyxmLGUsZCxjLGIsYSxhMCxhMSxhMixhMz1udWxsLGE0PWE1Lmxlbmd0aApp
+ZihhND49NSl7cz0oKEMueEIuVyhhNSw0KV41OCkqM3xDLnhCLlcoYTUsMCleMTAwfEMueEIuVyhhNSwx
+KV45N3xDLnhCLlcoYTUsMileMTE2fEMueEIuVyhhNSwzKV45Nyk+Pj4wCmlmKHM9PT0wKXJldHVybiBQ
+LktEKGE0PGE0P0MueEIuTmooYTUsMCxhNCk6YTUsNSxhMykuZ2xSKCkKZWxzZSBpZihzPT09MzIpcmV0
+dXJuIFAuS0QoQy54Qi5OaihhNSw1LGE0KSwwLGEzKS5nbFIoKX1yPVAuTzgoOCwwLCExLHQuUykKQy5O
+bS5ZNShyLDAsMCkKQy5ObS5ZNShyLDEsLTEpCkMuTm0uWTUociwyLC0xKQpDLk5tLlk1KHIsNywtMSkK
+Qy5ObS5ZNShyLDMsMCkKQy5ObS5ZNShyLDQsMCkKQy5ObS5ZNShyLDUsYTQpCkMuTm0uWTUociw2LGE0
+KQppZihQLlVCKGE1LDAsYTQsMCxyKT49MTQpQy5ObS5ZNShyLDcsYTQpCnE9clsxXQppZihxPj0wKWlm
+KFAuVUIoYTUsMCxxLDIwLHIpPT09MjApcls3XT1xCnA9clsyXSsxCm89clszXQpuPXJbNF0KbT1yWzVd
+Cmw9cls2XQppZihsPG0pbT1sCmlmKG48cCluPW0KZWxzZSBpZihuPD1xKW49cSsxCmlmKG88cClvPW4K
+az1yWzddPDAKaWYoaylpZihwPnErMyl7aj1hMwprPSExfWVsc2V7aT1vPjAKaWYoaSYmbysxPT09bil7
+aj1hMwprPSExfWVsc2V7aWYoIShtPGE0JiZtPT09bisyJiZDLnhCLlFpKGE1LCIuLiIsbikpKWg9bT5u
+KzImJkMueEIuUWkoYTUsIi8uLiIsbS0zKQplbHNlIGg9ITAKaWYoaCl7aj1hMwprPSExfWVsc2V7aWYo
+cT09PTQpaWYoQy54Qi5RaShhNSwiZmlsZSIsMCkpe2lmKHA8PTApe2lmKCFDLnhCLlFpKGE1LCIvIixu
+KSl7Zz0iZmlsZTovLy8iCnM9M31lbHNle2c9ImZpbGU6Ly8iCnM9Mn1hNT1nK0MueEIuTmooYTUsbixh
+NCkKcS09MAppPXMtMAptKz1pCmwrPWkKYTQ9YTUubGVuZ3RoCnA9NwpvPTcKbj03fWVsc2UgaWYobj09
+PW0peysrbApmPW0rMQphNT1DLnhCLmk3KGE1LG4sbSwiLyIpOysrYTQKbT1mfWo9ImZpbGUifWVsc2Ug
+aWYoQy54Qi5RaShhNSwiaHR0cCIsMCkpe2lmKGkmJm8rMz09PW4mJkMueEIuUWkoYTUsIjgwIixvKzEp
+KXtsLT0zCmU9bi0zCm0tPTMKYTU9Qy54Qi5pNyhhNSxvLG4sIiIpCmE0LT0zCm49ZX1qPSJodHRwIn1l
+bHNlIGo9YTMKZWxzZSBpZihxPT09NSYmQy54Qi5RaShhNSwiaHR0cHMiLDApKXtpZihpJiZvKzQ9PT1u
+JiZDLnhCLlFpKGE1LCI0NDMiLG8rMSkpe2wtPTQKZT1uLTQKbS09NAphNT1DLnhCLmk3KGE1LG8sbiwi
+IikKYTQtPTMKbj1lfWo9Imh0dHBzIn1lbHNlIGo9YTMKaz0hMH19fWVsc2Ugaj1hMwppZihrKXtpZihh
+NDxhNS5sZW5ndGgpe2E1PUMueEIuTmooYTUsMCxhNCkKcS09MApwLT0wCm8tPTAKbi09MAptLT0wCmwt
+PTB9cmV0dXJuIG5ldyBQLlVmKGE1LHEscCxvLG4sbSxsLGopfWlmKGo9PW51bGwpaWYocT4wKWo9UC5Q
+aShhNSwwLHEpCmVsc2V7aWYocT09PTApUC5SMyhhNSwwLCJJbnZhbGlkIGVtcHR5IHNjaGVtZSIpCmo9
IiJ9aWYocD4wKXtkPXErMwpjPWQ8cD9QLnpSKGE1LGQscC0xKToiIgpiPVAuT2UoYTUscCxvLCExKQpp
-PW8rMQppZihpPG4pe2E9SC5IcChKLmxkKGE1LGksbiksYTMpCmEwPVAud0IoYT09bnVsbD9ILnYoUC5y
-cigiSW52YWxpZCBwb3J0IixhNSxpKSk6YSxqKX1lbHNlIGEwPWEzfWVsc2V7YTA9YTMKYj1hMApjPSIi
-fWExPVAua2EoYTUsbixtLGEzLGosYiE9bnVsbCkKYTI9bTxsP1AubGUoYTUsbSsxLGwsYTMpOmEzCnJl
-dHVybiBQLkNnKGosYyxiLGEwLGExLGEyLGw8YTQ/UC50RyhhNSxsKzEsYTQpOmEzKX0sCk10OmZ1bmN0
-aW9uKGEpe0guaChhKQpyZXR1cm4gUC5rdShhLDAsYS5sZW5ndGgsQy54TSwhMSl9LApXWDpmdW5jdGlv
-bihhKXt2YXIgcz10Lk4KcmV0dXJuIEMuTm0uTjAoSC5RSShhLnNwbGl0KCImIiksdC5zKSxQLkZsKHMs
-cyksbmV3IFAubjEoQy54TSksdC5KKX0sCkhoOmZ1bmN0aW9uKGEsYixjKXt2YXIgcyxyLHEscCxvLG4s
-bT0iSVB2NCBhZGRyZXNzIHNob3VsZCBjb250YWluIGV4YWN0bHkgNCBwYXJ0cyIsbD0iZWFjaCBwYXJ0
-IG11c3QgYmUgaW4gdGhlIHJhbmdlIDAuLjI1NSIsaz1uZXcgUC5jUyhhKSxqPW5ldyBVaW50OEFycmF5
-KDQpCmZvcihzPWIscj1zLHE9MDtzPGM7KytzKXtwPUMueEIuTyhhLHMpCmlmKHAhPT00Nil7aWYoKHBe
-NDgpPjkpay4kMigiaW52YWxpZCBjaGFyYWN0ZXIiLHMpfWVsc2V7aWYocT09PTMpay4kMihtLHMpCm89
-UC5RQShDLnhCLk5qKGEscixzKSxudWxsKQppZihvPjI1NSlrLiQyKGwscikKbj1xKzEKaWYocT49NCly
-ZXR1cm4gSC5PSChqLHEpCmpbcV09bwpyPXMrMQpxPW59fWlmKHEhPT0zKWsuJDIobSxjKQpvPVAuUUEo
-Qy54Qi5OaihhLHIsYyksbnVsbCkKaWYobz4yNTUpay4kMihsLHIpCmlmKHE+PTQpcmV0dXJuIEguT0go
-aixxKQpqW3FdPW8KcmV0dXJuIGp9LAplZzpmdW5jdGlvbihhLGIsYTApe3ZhciBzLHIscSxwLG8sbixt
-LGwsayxqLGksaCxnLGYsZSxkPW5ldyBQLlZDKGEpLGM9bmV3IFAuSlQoZCxhKQppZihhLmxlbmd0aDwy
-KWQuJDEoImFkZHJlc3MgaXMgdG9vIHNob3J0IikKcz1ILlFJKFtdLHQuYSkKZm9yKHI9YixxPXIscD0h
-MSxvPSExO3I8YTA7KytyKXtuPUMueEIuTyhhLHIpCmlmKG49PT01OCl7aWYocj09PWIpeysrcgppZihD
-LnhCLk8oYSxyKSE9PTU4KWQuJDIoImludmFsaWQgc3RhcnQgY29sb24uIixyKQpxPXJ9aWYocj09PXEp
-e2lmKHApZC4kMigib25seSBvbmUgd2lsZGNhcmQgYDo6YCBpcyBhbGxvd2VkIixyKQpDLk5tLmkocywt
-MSkKcD0hMH1lbHNlIEMuTm0uaShzLGMuJDIocSxyKSkKcT1yKzF9ZWxzZSBpZihuPT09NDYpbz0hMH1p
-ZihzLmxlbmd0aD09PTApZC4kMSgidG9vIGZldyBwYXJ0cyIpCm09cT09PWEwCmw9Qy5ObS5ncloocykK
-aWYobSYmbCE9PS0xKWQuJDIoImV4cGVjdGVkIGEgcGFydCBhZnRlciBsYXN0IGA6YCIsYTApCmlmKCFt
-KWlmKCFvKUMuTm0uaShzLGMuJDIocSxhMCkpCmVsc2V7az1QLkhoKGEscSxhMCkKQy5ObS5pKHMsKGtb
-MF08PDh8a1sxXSk+Pj4wKQpDLk5tLmkocywoa1syXTw8OHxrWzNdKT4+PjApfWlmKHApe2lmKHMubGVu
-Z3RoPjcpZC4kMSgiYW4gYWRkcmVzcyB3aXRoIGEgd2lsZGNhcmQgbXVzdCBoYXZlIGxlc3MgdGhhbiA3
-IHBhcnRzIil9ZWxzZSBpZihzLmxlbmd0aCE9PTgpZC4kMSgiYW4gYWRkcmVzcyB3aXRob3V0IGEgd2ls
-ZGNhcmQgbXVzdCBjb250YWluIGV4YWN0bHkgOCBwYXJ0cyIpCmo9bmV3IFVpbnQ4QXJyYXkoMTYpCmZv
-cihsPXMubGVuZ3RoLGk9OS1sLHI9MCxoPTA7cjxsOysrcil7Zz1zW3JdCmlmKGc9PT0tMSlmb3IoZj0w
-O2Y8aTsrK2Ype2lmKGg8MHx8aD49MTYpcmV0dXJuIEguT0goaixoKQpqW2hdPTAKZT1oKzEKaWYoZT49
-MTYpcmV0dXJuIEguT0goaixlKQpqW2VdPTAKaCs9Mn1lbHNle2U9Qy5qbi53RyhnLDgpCmlmKGg8MHx8
-aD49MTYpcmV0dXJuIEguT0goaixoKQpqW2hdPWUKZT1oKzEKaWYoZT49MTYpcmV0dXJuIEguT0goaixl
-KQpqW2VdPWcmMjU1CmgrPTJ9fXJldHVybiBqfSwKQ2c6ZnVuY3Rpb24oYSxiLGMsZCxlLGYsZyl7cmV0
-dXJuIG5ldyBQLkRuKGEsYixjLGQsZSxmLGcpfSwKS0w6ZnVuY3Rpb24oYSxiLGMsZCxlLGYsZyl7dmFy
-IHMscixxLHAsbyxuCmY9Zj09bnVsbD8iIjpQLlBpKGYsMCxmLmxlbmd0aCkKZz1QLnpSKGcsMCxnPT1u
-dWxsPzA6Zy5sZW5ndGgpCmE9UC5PZShhLDAsYT09bnVsbD8wOmEubGVuZ3RoLCExKQpzPVAubGUobnVs
-bCwwLDAsZSkKcj1QLnRHKG51bGwsMCwwKQpkPVAud0IoZCxmKQpxPWY9PT0iZmlsZSIKaWYoYT09bnVs
-bClwPWcubGVuZ3RoIT09MHx8ZCE9bnVsbHx8cQplbHNlIHA9ITEKaWYocClhPSIiCnA9YT09bnVsbApv
-PSFwCmI9UC5rYShiLDAsYj09bnVsbD8wOmIubGVuZ3RoLGMsZixvKQpuPWYubGVuZ3RoPT09MAppZihu
-JiZwJiYhQy54Qi5uQyhiLCIvIikpYj1QLndGKGIsIW58fG8pCmVsc2UgYj1QLnhlKGIpCnJldHVybiBQ
-LkNnKGYsZyxwJiZDLnhCLm5DKGIsIi8vIik/IiI6YSxkLGIscyxyKX0sCndLOmZ1bmN0aW9uKGEpe2lm
-KGE9PT0iaHR0cCIpcmV0dXJuIDgwCmlmKGE9PT0iaHR0cHMiKXJldHVybiA0NDMKcmV0dXJuIDB9LApO
-UjpmdW5jdGlvbihhLGIpe3ZhciBzLHIscSxwLG8sbgpmb3Iocz1hLmxlbmd0aCxyPTA7cjxzOysrcil7
-cT1DLnhCLlcoYSxyKQpwPUMueEIuVyhiLHIpCm89cV5wCmlmKG8hPT0wKXtpZihvPT09MzIpe249cHxv
-CmlmKDk3PD1uJiZuPD0xMjIpY29udGludWV9cmV0dXJuITF9fXJldHVybiEwfSwKUjM6ZnVuY3Rpb24o
-YSxiLGMpe3Rocm93IEguYihQLnJyKGMsYSxiKSl9LApYZDpmdW5jdGlvbihhLGIsYyxkKXt2YXIgcyxy
-LHEscCxvLG4sbSxsLGssaixpLGg9bnVsbCxnPWIubGVuZ3RoCmlmKGchPT0wKXtxPTAKd2hpbGUoITAp
-e2lmKCEocTxnKSl7cz0iIgpyPTAKYnJlYWt9aWYoQy54Qi5XKGIscSk9PT02NCl7cz1DLnhCLk5qKGIs
-MCxxKQpyPXErMQpicmVha30rK3F9aWYocjxnJiZDLnhCLlcoYixyKT09PTkxKXtmb3IocD1yLG89LTE7
-cDxnOysrcCl7bj1DLnhCLlcoYixwKQppZihuPT09MzcmJm88MCl7bT1DLnhCLlFpKGIsIjI1IixwKzEp
-P3ArMjpwCm89cApwPW19ZWxzZSBpZihuPT09OTMpYnJlYWt9aWYocD09PWcpdGhyb3cgSC5iKFAucnIo
-IkludmFsaWQgSVB2NiBob3N0IGVudHJ5LiIsYixyKSkKbD1vPDA/cDpvClAuZWcoYixyKzEsbCk7Kytw
-CmlmKHAhPT1nJiZDLnhCLlcoYixwKSE9PTU4KXRocm93IEguYihQLnJyKCJJbnZhbGlkIGVuZCBvZiBh
-dXRob3JpdHkiLGIscCkpfWVsc2UgcD1yCndoaWxlKCEwKXtpZighKHA8Zykpe2s9aApicmVha31pZihD
-LnhCLlcoYixwKT09PTU4KXtqPUMueEIueW4oYixwKzEpCms9ai5sZW5ndGghPT0wP1AuUUEoaixoKTpo
-CmJyZWFrfSsrcH1pPUMueEIuTmooYixyLHApfWVsc2V7az1oCmk9awpzPSIifXJldHVybiBQLktMKGks
-aCxILlFJKGMuc3BsaXQoIi8iKSx0LnMpLGssZCxhLHMpfSwKa0U6ZnVuY3Rpb24oYSxiKXt2YXIgcyxy
-CmZvcihzPUouSVQoYSk7cy5GKCk7KXtyPXMuZ2woKQpyLnRvU3RyaW5nCmlmKEguU1EociwiLyIsMCkp
-e3M9UC5MNCgiSWxsZWdhbCBwYXRoIGNoYXJhY3RlciAiK3IpCnRocm93IEguYihzKX19fSwKSE46ZnVu
-Y3Rpb24oYSxiLGMpe3ZhciBzLHIscQpmb3Iocz1KLkE1KGEsYykscz1zLmdtKHMpO3MuRigpOyl7cj1z
-LmdsKCkKcT1QLm51KCdbIiovOjw+P1xcXFx8XScpCnIudG9TdHJpbmcKaWYoSC5TUShyLHEsMCkpe3M9
-UC5MNCgiSWxsZWdhbCBjaGFyYWN0ZXIgaW4gcGF0aDogIityKQp0aHJvdyBILmIocyl9fX0sCnJnOmZ1
-bmN0aW9uKGEsYil7dmFyIHMKaWYoISg2NTw9YSYmYTw9OTApKXM9OTc8PWEmJmE8PTEyMgplbHNlIHM9
-ITAKaWYocylyZXR1cm4Kcz1QLkw0KCJJbGxlZ2FsIGRyaXZlIGxldHRlciAiK1AuT28oYSkpCnRocm93
-IEguYihzKX0sCndCOmZ1bmN0aW9uKGEsYil7aWYoYSE9bnVsbCYmYT09PVAud0soYikpcmV0dXJuIG51
-bGwKcmV0dXJuIGF9LApPZTpmdW5jdGlvbihhLGIsYyxkKXt2YXIgcyxyLHEscCxvLG4KaWYoYT09bnVs
-bClyZXR1cm4gbnVsbAppZihiPT09YylyZXR1cm4iIgppZihDLnhCLk8oYSxiKT09PTkxKXtzPWMtMQpp
-ZihDLnhCLk8oYSxzKSE9PTkzKXtQLlIzKGEsYiwiTWlzc2luZyBlbmQgYF1gIHRvIG1hdGNoIGBbYCBp
-biBob3N0IikKSC5CaSh1LmcpfXI9YisxCnE9UC50byhhLHIscykKaWYocTxzKXtwPXErMQpvPVAuT0Eo
-YSxDLnhCLlFpKGEsIjI1IixwKT9xKzM6cCxzLCIlMjUiKX1lbHNlIG89IiIKUC5lZyhhLHIscSkKcmV0
-dXJuIEMueEIuTmooYSxiLHEpLnRvTG93ZXJDYXNlKCkrbysiXSJ9Zm9yKG49YjtuPGM7KytuKWlmKEMu
-eEIuTyhhLG4pPT09NTgpe3E9Qy54Qi5YVShhLCIlIixiKQpxPXE+PWImJnE8Yz9xOmMKaWYocTxjKXtw
-PXErMQpvPVAuT0EoYSxDLnhCLlFpKGEsIjI1IixwKT9xKzM6cCxjLCIlMjUiKX1lbHNlIG89IiIKUC5l
-ZyhhLGIscSkKcmV0dXJuIlsiK0MueEIuTmooYSxiLHEpK28rIl0ifXJldHVybiBQLk9MKGEsYixjKX0s
-CnRvOmZ1bmN0aW9uKGEsYixjKXt2YXIgcz1DLnhCLlhVKGEsIiUiLGIpCnJldHVybiBzPj1iJiZzPGM/
-czpjfSwKT0E6ZnVuY3Rpb24oYSxiLGMsZCl7dmFyIHMscixxLHAsbyxuLG0sbCxrLGosaT1kIT09IiI/
-bmV3IFAuTShkKTpudWxsCmZvcihzPWIscj1zLHE9ITA7czxjOyl7cD1DLnhCLk8oYSxzKQppZihwPT09
-Mzcpe289UC5ydihhLHMsITApCm49bz09bnVsbAppZihuJiZxKXtzKz0zCmNvbnRpbnVlfWlmKGk9PW51
-bGwpaT1uZXcgUC5NKCIiKQptPWkuYSs9Qy54Qi5OaihhLHIscykKaWYobilvPUMueEIuTmooYSxzLHMr
-MykKZWxzZSBpZihvPT09IiUiKXtQLlIzKGEscywiWm9uZUlEIHNob3VsZCBub3QgY29udGFpbiAlIGFu
-eW1vcmUiKQpILkJpKHUuZyl9aS5hPW0rbwpzKz0zCnI9cwpxPSEwfWVsc2V7aWYocDwxMjcpe249cD4+
-PjQKaWYobj49OClyZXR1cm4gSC5PSChDLkYzLG4pCm49KEMuRjNbbl0mMTw8KHAmMTUpKSE9PTB9ZWxz
-ZSBuPSExCmlmKG4pe2lmKHEmJjY1PD1wJiY5MD49cCl7aWYoaT09bnVsbClpPW5ldyBQLk0oIiIpCmlm
-KHI8cyl7aS5hKz1DLnhCLk5qKGEscixzKQpyPXN9cT0hMX0rK3N9ZWxzZXtpZigocCY2NDUxMik9PT01
-NTI5NiYmcysxPGMpe2w9Qy54Qi5PKGEscysxKQppZigobCY2NDUxMik9PT01NjMyMCl7cD0ocCYxMDIz
-KTw8MTB8bCYxMDIzfDY1NTM2Cms9Mn1lbHNlIGs9MX1lbHNlIGs9MQpqPUMueEIuTmooYSxyLHMpCmlm
-KGk9PW51bGwpe2k9bmV3IFAuTSgiIikKbj1pfWVsc2Ugbj1pCm4uYSs9agpuLmErPVAuelgocCkKcys9
-awpyPXN9fX1pZihpPT1udWxsKXJldHVybiBDLnhCLk5qKGEsYixjKQppZihyPGMpaS5hKz1DLnhCLk5q
-KGEscixjKQpuPWkuYQpyZXR1cm4gbi5jaGFyQ29kZUF0KDApPT0wP246bn0sCk9MOmZ1bmN0aW9uKGEs
-YixjKXt2YXIgcyxyLHEscCxvLG4sbSxsLGssaixpCmZvcihzPWIscj1zLHE9bnVsbCxwPSEwO3M8Yzsp
-e289Qy54Qi5PKGEscykKaWYobz09PTM3KXtuPVAucnYoYSxzLCEwKQptPW49PW51bGwKaWYobSYmcCl7
-cys9Mwpjb250aW51ZX1pZihxPT1udWxsKXE9bmV3IFAuTSgiIikKbD1DLnhCLk5qKGEscixzKQprPXEu
-YSs9IXA/bC50b0xvd2VyQ2FzZSgpOmwKaWYobSl7bj1DLnhCLk5qKGEscyxzKzMpCmo9M31lbHNlIGlm
-KG49PT0iJSIpe249IiUyNSIKaj0xfWVsc2Ugaj0zCnEuYT1rK24Kcys9agpyPXMKcD0hMH1lbHNle2lm
-KG88MTI3KXttPW8+Pj40CmlmKG0+PTgpcmV0dXJuIEguT0goQy5lYSxtKQptPShDLmVhW21dJjE8PChv
-JjE1KSkhPT0wfWVsc2UgbT0hMQppZihtKXtpZihwJiY2NTw9byYmOTA+PW8pe2lmKHE9PW51bGwpcT1u
-ZXcgUC5NKCIiKQppZihyPHMpe3EuYSs9Qy54Qi5OaihhLHIscykKcj1zfXA9ITF9KytzfWVsc2V7aWYo
-bzw9OTMpe209bz4+PjQKaWYobT49OClyZXR1cm4gSC5PSChDLmFrLG0pCm09KEMuYWtbbV0mMTw8KG8m
-MTUpKSE9PTB9ZWxzZSBtPSExCmlmKG0pe1AuUjMoYSxzLCJJbnZhbGlkIGNoYXJhY3RlciIpCkguQmko
-dS5nKX1lbHNle2lmKChvJjY0NTEyKT09PTU1Mjk2JiZzKzE8Yyl7aT1DLnhCLk8oYSxzKzEpCmlmKChp
-JjY0NTEyKT09PTU2MzIwKXtvPShvJjEwMjMpPDwxMHxpJjEwMjN8NjU1MzYKaj0yfWVsc2Ugaj0xfWVs
-c2Ugaj0xCmw9Qy54Qi5OaihhLHIscykKaWYoIXApbD1sLnRvTG93ZXJDYXNlKCkKaWYocT09bnVsbCl7
-cT1uZXcgUC5NKCIiKQptPXF9ZWxzZSBtPXEKbS5hKz1sCm0uYSs9UC56WChvKQpzKz1qCnI9c319fX1p
-ZihxPT1udWxsKXJldHVybiBDLnhCLk5qKGEsYixjKQppZihyPGMpe2w9Qy54Qi5OaihhLHIsYykKcS5h
-Kz0hcD9sLnRvTG93ZXJDYXNlKCk6bH1tPXEuYQpyZXR1cm4gbS5jaGFyQ29kZUF0KDApPT0wP206bX0s
-ClBpOmZ1bmN0aW9uKGEsYixjKXt2YXIgcyxyLHEscCxvPXUuZwppZihiPT09YylyZXR1cm4iIgppZigh
-UC5FdChKLlF6KGEsYikpKXtQLlIzKGEsYiwiU2NoZW1lIG5vdCBzdGFydGluZyB3aXRoIGFscGhhYmV0
-aWMgY2hhcmFjdGVyIikKSC5CaShvKX1mb3Iocz1iLHI9ITE7czxjOysrcyl7cT1DLnhCLlcoYSxzKQpp
-ZihxPDEyOCl7cD1xPj4+NAppZihwPj04KXJldHVybiBILk9IKEMubUsscCkKcD0oQy5tS1twXSYxPDwo
-cSYxNSkpIT09MH1lbHNlIHA9ITEKaWYoIXApe1AuUjMoYSxzLCJJbGxlZ2FsIHNjaGVtZSBjaGFyYWN0
-ZXIiKQpILkJpKG8pfWlmKDY1PD1xJiZxPD05MClyPSEwfWE9Qy54Qi5OaihhLGIsYykKcmV0dXJuIFAu
-WWEocj9hLnRvTG93ZXJDYXNlKCk6YSl9LApZYTpmdW5jdGlvbihhKXtpZihhPT09Imh0dHAiKXJldHVy
-biJodHRwIgppZihhPT09ImZpbGUiKXJldHVybiJmaWxlIgppZihhPT09Imh0dHBzIilyZXR1cm4iaHR0
-cHMiCmlmKGE9PT0icGFja2FnZSIpcmV0dXJuInBhY2thZ2UiCnJldHVybiBhfSwKelI6ZnVuY3Rpb24o
-YSxiLGMpe2lmKGE9PW51bGwpcmV0dXJuIiIKcmV0dXJuIFAuUEkoYSxiLGMsQy50bywhMSl9LAprYTpm
-dW5jdGlvbihhLGIsYyxkLGUsZil7dmFyIHMscixxPWU9PT0iZmlsZSIscD1xfHxmCmlmKGE9PW51bGwp
-e2lmKGQ9PW51bGwpcmV0dXJuIHE/Ii8iOiIiCnM9SC50NihkKQpyPW5ldyBILmxKKGQscy5DKCJxVSgx
-KSIpLmEobmV3IFAuUlooKSkscy5DKCJsSjwxLHFVPiIpKS5rKDAsIi8iKX1lbHNlIGlmKGQhPW51bGwp
-dGhyb3cgSC5iKFAueFkoIkJvdGggcGF0aCBhbmQgcGF0aFNlZ21lbnRzIHNwZWNpZmllZCIpKQplbHNl
-IHI9UC5QSShhLGIsYyxDLldkLCEwKQppZihyLmxlbmd0aD09PTApe2lmKHEpcmV0dXJuIi8ifWVsc2Ug
-aWYocCYmIUMueEIubkMociwiLyIpKXI9Ii8iK3IKcmV0dXJuIFAuSnIocixlLGYpfSwKSnI6ZnVuY3Rp
-b24oYSxiLGMpe3ZhciBzPWIubGVuZ3RoPT09MAppZihzJiYhYyYmIUMueEIubkMoYSwiLyIpKXJldHVy
-biBQLndGKGEsIXN8fGMpCnJldHVybiBQLnhlKGEpfSwKbGU6ZnVuY3Rpb24oYSxiLGMsZCl7dmFyIHMs
-cj17fQppZihhIT1udWxsKXtpZihkIT1udWxsKXRocm93IEguYihQLnhZKCJCb3RoIHF1ZXJ5IGFuZCBx
-dWVyeVBhcmFtZXRlcnMgc3BlY2lmaWVkIikpCnJldHVybiBQLlBJKGEsYixjLEMuVkMsITApfWlmKGQ9
-PW51bGwpcmV0dXJuIG51bGwKcz1uZXcgUC5NKCIiKQpyLmE9IiIKZC5LKDAsbmV3IFAueTUobmV3IFAu
-TUUocixzKSkpCnI9cy5hCnJldHVybiByLmNoYXJDb2RlQXQoMCk9PTA/cjpyfSwKdEc6ZnVuY3Rpb24o
-YSxiLGMpe2lmKGE9PW51bGwpcmV0dXJuIG51bGwKcmV0dXJuIFAuUEkoYSxiLGMsQy5WQywhMCl9LApy
-djpmdW5jdGlvbihhLGIsYyl7dmFyIHMscixxLHAsbyxuPWIrMgppZihuPj1hLmxlbmd0aClyZXR1cm4i
-JSIKcz1DLnhCLk8oYSxiKzEpCnI9Qy54Qi5PKGEsbikKcT1ILm9vKHMpCnA9SC5vbyhyKQppZihxPDB8
-fHA8MClyZXR1cm4iJSIKbz1xKjE2K3AKaWYobzwxMjcpe249Qy5qbi53RyhvLDQpCmlmKG4+PTgpcmV0
-dXJuIEguT0goQy5GMyxuKQpuPShDLkYzW25dJjE8PChvJjE1KSkhPT0wfWVsc2Ugbj0hMQppZihuKXJl
-dHVybiBILkx3KGMmJjY1PD1vJiY5MD49bz8ob3wzMik+Pj4wOm8pCmlmKHM+PTk3fHxyPj05NylyZXR1
-cm4gQy54Qi5OaihhLGIsYiszKS50b1VwcGVyQ2FzZSgpCnJldHVybiBudWxsfSwKelg6ZnVuY3Rpb24o
-YSl7dmFyIHMscixxLHAsbyxuLG0sbCxrPSIwMTIzNDU2Nzg5QUJDREVGIgppZihhPDEyOCl7cz1uZXcg
-VWludDhBcnJheSgzKQpzWzBdPTM3CnNbMV09Qy54Qi5XKGssYT4+PjQpCnNbMl09Qy54Qi5XKGssYSYx
-NSl9ZWxzZXtpZihhPjIwNDcpaWYoYT42NTUzNSl7cj0yNDAKcT00fWVsc2V7cj0yMjQKcT0zfWVsc2V7
-cj0xOTIKcT0yfXA9MypxCnM9bmV3IFVpbnQ4QXJyYXkocCkKZm9yKG89MDstLXEscT49MDtyPTEyOCl7
-bj1DLmpuLmJmKGEsNipxKSY2M3xyCmlmKG8+PXApcmV0dXJuIEguT0gocyxvKQpzW29dPTM3Cm09bysx
-Cmw9Qy54Qi5XKGssbj4+PjQpCmlmKG0+PXApcmV0dXJuIEguT0gocyxtKQpzW21dPWwKbD1vKzIKbT1D
-LnhCLlcoayxuJjE1KQppZihsPj1wKXJldHVybiBILk9IKHMsbCkKc1tsXT1tCm8rPTN9fXJldHVybiBQ
-LkhNKHMsMCxudWxsKX0sClBJOmZ1bmN0aW9uKGEsYixjLGQsZSl7dmFyIHM9UC5VbChhLGIsYyxkLGUp
-CnJldHVybiBzPT1udWxsP0MueEIuTmooYSxiLGMpOnN9LApVbDpmdW5jdGlvbihhLGIsYyxkLGUpe3Zh
-ciBzLHIscSxwLG8sbixtLGwsayxqPW51bGwKZm9yKHM9IWUscj1iLHE9cixwPWo7cjxjOyl7bz1DLnhC
-Lk8oYSxyKQppZihvPDEyNyl7bj1vPj4+NAppZihuPj04KXJldHVybiBILk9IKGQsbikKbj0oZFtuXSYx
-PDwobyYxNSkpIT09MH1lbHNlIG49ITEKaWYobikrK3IKZWxzZXtpZihvPT09Mzcpe209UC5ydihhLHIs
-ITEpCmlmKG09PW51bGwpe3IrPTMKY29udGludWV9aWYoIiUiPT09bSl7bT0iJTI1IgpsPTF9ZWxzZSBs
-PTN9ZWxzZXtpZihzKWlmKG88PTkzKXtuPW8+Pj40CmlmKG4+PTgpcmV0dXJuIEguT0goQy5hayxuKQpu
-PShDLmFrW25dJjE8PChvJjE1KSkhPT0wfWVsc2Ugbj0hMQplbHNlIG49ITEKaWYobil7UC5SMyhhLHIs
-IkludmFsaWQgY2hhcmFjdGVyIikKSC5CaSh1LmcpCmw9agptPWx9ZWxzZXtpZigobyY2NDUxMik9PT01
-NTI5Nil7bj1yKzEKaWYobjxjKXtrPUMueEIuTyhhLG4pCmlmKChrJjY0NTEyKT09PTU2MzIwKXtvPShv
-JjEwMjMpPDwxMHxrJjEwMjN8NjU1MzYKbD0yfWVsc2UgbD0xfWVsc2UgbD0xfWVsc2UgbD0xCm09UC56
-WChvKX19aWYocD09bnVsbCl7cD1uZXcgUC5NKCIiKQpuPXB9ZWxzZSBuPXAKbi5hKz1DLnhCLk5qKGEs
-cSxyKQpuLmErPUguRWoobSkKaWYodHlwZW9mIGwhPT0ibnVtYmVyIilyZXR1cm4gSC5wWShsKQpyKz1s
-CnE9cn19aWYocD09bnVsbClyZXR1cm4gagppZihxPGMpcC5hKz1DLnhCLk5qKGEscSxjKQpzPXAuYQpy
-ZXR1cm4gcy5jaGFyQ29kZUF0KDApPT0wP3M6c30sCnlCOmZ1bmN0aW9uKGEpe2lmKEMueEIubkMoYSwi
-LiIpKXJldHVybiEwCnJldHVybiBDLnhCLk9ZKGEsIi8uIikhPT0tMX0sCnhlOmZ1bmN0aW9uKGEpe3Zh
-ciBzLHIscSxwLG8sbixtCmlmKCFQLnlCKGEpKXJldHVybiBhCnM9SC5RSShbXSx0LnMpCmZvcihyPWEu
-c3BsaXQoIi8iKSxxPXIubGVuZ3RoLHA9ITEsbz0wO288cTsrK28pe249cltvXQppZihKLlJNKG4sIi4u
-Iikpe209cy5sZW5ndGgKaWYobSE9PTApe2lmKDA+PW0pcmV0dXJuIEguT0gocywtMSkKcy5wb3AoKQpp
-ZihzLmxlbmd0aD09PTApQy5ObS5pKHMsIiIpfXA9ITB9ZWxzZSBpZigiLiI9PT1uKXA9ITAKZWxzZXtD
-Lk5tLmkocyxuKQpwPSExfX1pZihwKUMuTm0uaShzLCIiKQpyZXR1cm4gQy5ObS5rKHMsIi8iKX0sCndG
-OmZ1bmN0aW9uKGEsYil7dmFyIHMscixxLHAsbyxuCmlmKCFQLnlCKGEpKXJldHVybiFiP1AuQzEoYSk6
-YQpzPUguUUkoW10sdC5zKQpmb3Iocj1hLnNwbGl0KCIvIikscT1yLmxlbmd0aCxwPSExLG89MDtvPHE7
-KytvKXtuPXJbb10KaWYoIi4uIj09PW4paWYocy5sZW5ndGghPT0wJiZDLk5tLmdyWihzKSE9PSIuLiIp
-e2lmKDA+PXMubGVuZ3RoKXJldHVybiBILk9IKHMsLTEpCnMucG9wKCkKcD0hMH1lbHNle0MuTm0uaShz
-LCIuLiIpCnA9ITF9ZWxzZSBpZigiLiI9PT1uKXA9ITAKZWxzZXtDLk5tLmkocyxuKQpwPSExfX1yPXMu
-bGVuZ3RoCmlmKHIhPT0wKWlmKHI9PT0xKXtpZigwPj1yKXJldHVybiBILk9IKHMsMCkKcj1zWzBdLmxl
-bmd0aD09PTB9ZWxzZSByPSExCmVsc2Ugcj0hMAppZihyKXJldHVybiIuLyIKaWYocHx8Qy5ObS5ncloo
-cyk9PT0iLi4iKUMuTm0uaShzLCIiKQppZighYil7aWYoMD49cy5sZW5ndGgpcmV0dXJuIEguT0gocyww
-KQpDLk5tLlk1KHMsMCxQLkMxKHNbMF0pKX1yZXR1cm4gQy5ObS5rKHMsIi8iKX0sCkMxOmZ1bmN0aW9u
-KGEpe3ZhciBzLHIscSxwPWEubGVuZ3RoCmlmKHA+PTImJlAuRXQoSi5ReihhLDApKSlmb3Iocz0xO3M8
-cDsrK3Mpe3I9Qy54Qi5XKGEscykKaWYocj09PTU4KXJldHVybiBDLnhCLk5qKGEsMCxzKSsiJTNBIitD
-LnhCLnluKGEscysxKQppZihyPD0xMjcpe3E9cj4+PjQKaWYocT49OClyZXR1cm4gSC5PSChDLm1LLHEp
-CnE9KEMubUtbcV0mMTw8KHImMTUpKT09PTB9ZWxzZSBxPSEwCmlmKHEpYnJlYWt9cmV0dXJuIGF9LAp1
-ajpmdW5jdGlvbihhLGIpe2lmKGEuaEIoInBhY2thZ2UiKSYmYS5jPT1udWxsKXJldHVybiBQLmZGKGIs
-MCxiLmxlbmd0aCkKcmV0dXJuLTF9LAptbjpmdW5jdGlvbihhKXt2YXIgcyxyLHEscD1hLmdGaigpLG89
-Si5VNihwKQppZihvLmdBKHApPjAmJkouSG0oby5xKHAsMCkpPT09MiYmSi5hNihvLnEocCwwKSwxKT09
-PTU4KXtQLnJnKEouYTYoby5xKHAsMCksMCksITEpClAuSE4ocCwhMSwxKQpzPSEwfWVsc2V7UC5ITihw
-LCExLDApCnM9ITF9cj1hLmd0VCgpJiYhcz8iXFwiOiIiCmlmKGEuZ2NqKCkpe3E9YS5nSmYoYSkKaWYo
-cS5sZW5ndGghPT0wKXI9cisiXFwiK3ErIlxcIn1yPVAubChyLHAsIlxcIikKbz1zJiZvLmdBKHApPT09
-MT9yKyJcXCI6cgpyZXR1cm4gby5jaGFyQ29kZUF0KDApPT0wP286b30sCkloOmZ1bmN0aW9uKGEsYil7
-dmFyIHMscixxCmZvcihzPTAscj0wO3I8MjsrK3Ipe3E9Qy54Qi5XKGEsYityKQppZig0ODw9cSYmcTw9
-NTcpcz1zKjE2K3EtNDgKZWxzZXtxfD0zMgppZig5Nzw9cSYmcTw9MTAyKXM9cyoxNitxLTg3CmVsc2Ug
-dGhyb3cgSC5iKFAueFkoIkludmFsaWQgVVJMIGVuY29kaW5nIikpfX1yZXR1cm4gc30sCmt1OmZ1bmN0
-aW9uKGEsYixjLGQsZSl7dmFyIHMscixxLHAsbz1KLnJZKGEpLG49Ygp3aGlsZSghMCl7aWYoIShuPGMp
-KXtzPSEwCmJyZWFrfXI9by5XKGEsbikKaWYocjw9MTI3KWlmKHIhPT0zNylxPWUmJnI9PT00MwplbHNl
-IHE9ITAKZWxzZSBxPSEwCmlmKHEpe3M9ITEKYnJlYWt9KytufWlmKHMpe2lmKEMueE0hPT1kKXE9ITEK
-ZWxzZSBxPSEwCmlmKHEpcmV0dXJuIG8uTmooYSxiLGMpCmVsc2UgcD1uZXcgSC5xaihvLk5qKGEsYixj
-KSl9ZWxzZXtwPUguUUkoW10sdC5hKQpmb3Iobj1iO248YzsrK24pe3I9by5XKGEsbikKaWYocj4xMjcp
-dGhyb3cgSC5iKFAueFkoIklsbGVnYWwgcGVyY2VudCBlbmNvZGluZyBpbiBVUkkiKSkKaWYocj09PTM3
-KXtpZihuKzM+YS5sZW5ndGgpdGhyb3cgSC5iKFAueFkoIlRydW5jYXRlZCBVUkkiKSkKQy5ObS5pKHAs
-UC5JaChhLG4rMSkpCm4rPTJ9ZWxzZSBpZihlJiZyPT09NDMpQy5ObS5pKHAsMzIpCmVsc2UgQy5ObS5p
-KHAscil9fXQuTC5hKHApCnJldHVybiBDLm9FLldKKHApfSwKRXQ6ZnVuY3Rpb24oYSl7dmFyIHM9YXwz
-MgpyZXR1cm4gOTc8PXMmJnM8PTEyMn0sCktEOmZ1bmN0aW9uKGEsYixjKXt2YXIgcyxyLHEscCxvLG4s
-bSxsLGs9IkludmFsaWQgTUlNRSB0eXBlIixqPUguUUkoW2ItMV0sdC5hKQpmb3Iocz1hLmxlbmd0aCxy
-PWIscT0tMSxwPW51bGw7cjxzOysrcil7cD1DLnhCLlcoYSxyKQppZihwPT09NDR8fHA9PT01OSlicmVh
-awppZihwPT09NDcpe2lmKHE8MCl7cT1yCmNvbnRpbnVlfXRocm93IEguYihQLnJyKGssYSxyKSl9fWlm
-KHE8MCYmcj5iKXRocm93IEguYihQLnJyKGssYSxyKSkKZm9yKDtwIT09NDQ7KXtDLk5tLmkoaixyKTsr
-K3IKZm9yKG89LTE7cjxzOysrcil7cD1DLnhCLlcoYSxyKQppZihwPT09NjEpe2lmKG88MClvPXJ9ZWxz
-ZSBpZihwPT09NTl8fHA9PT00NClicmVha31pZihvPj0wKUMuTm0uaShqLG8pCmVsc2V7bj1DLk5tLmdy
-WihqKQppZihwIT09NDR8fHIhPT1uKzd8fCFDLnhCLlFpKGEsImJhc2U2NCIsbisxKSl0aHJvdyBILmIo
-UC5ycigiRXhwZWN0aW5nICc9JyIsYSxyKSkKYnJlYWt9fUMuTm0uaShqLHIpCm09cisxCmlmKChqLmxl
-bmd0aCYxKT09PTEpYT1DLmg5LnlyKGEsbSxzKQplbHNle2w9UC5VbChhLG0scyxDLlZDLCEwKQppZihs
-IT1udWxsKWE9Qy54Qi5pNyhhLG0scyxsKX1yZXR1cm4gbmV3IFAuUEUoYSxqLGMpfSwKS046ZnVuY3Rp
-b24oKXt2YXIgcyxyLHEscCxvLG4sbT0iMDEyMzQ1Njc4OUFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFla
-YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXotLl9+ISQmJygpKissOz0iLGw9Ii4iLGs9IjoiLGo9Ii8i
-LGk9Ij8iLGg9IiMiLGc9SC5RSShuZXcgQXJyYXkoMjIpLHQuZ04pCmZvcihzPTA7czwyMjsrK3MpZ1tz
-XT1uZXcgVWludDhBcnJheSg5NikKcj1uZXcgUC55SShnKQpxPW5ldyBQLmM2KCkKcD1uZXcgUC5xZCgp
-Cm89dC5nYwpuPW8uYShyLiQyKDAsMjI1KSkKcS4kMyhuLG0sMSkKcS4kMyhuLGwsMTQpCnEuJDMobixr
-LDM0KQpxLiQzKG4saiwzKQpxLiQzKG4saSwxNzIpCnEuJDMobixoLDIwNSkKbj1vLmEoci4kMigxNCwy
-MjUpKQpxLiQzKG4sbSwxKQpxLiQzKG4sbCwxNSkKcS4kMyhuLGssMzQpCnEuJDMobixqLDIzNCkKcS4k
-MyhuLGksMTcyKQpxLiQzKG4saCwyMDUpCm49by5hKHIuJDIoMTUsMjI1KSkKcS4kMyhuLG0sMSkKcS4k
-MyhuLCIlIiwyMjUpCnEuJDMobixrLDM0KQpxLiQzKG4saiw5KQpxLiQzKG4saSwxNzIpCnEuJDMobixo
-LDIwNSkKbj1vLmEoci4kMigxLDIyNSkpCnEuJDMobixtLDEpCnEuJDMobixrLDM0KQpxLiQzKG4saiwx
-MCkKcS4kMyhuLGksMTcyKQpxLiQzKG4saCwyMDUpCm49by5hKHIuJDIoMiwyMzUpKQpxLiQzKG4sbSwx
-MzkpCnEuJDMobixqLDEzMSkKcS4kMyhuLGwsMTQ2KQpxLiQzKG4saSwxNzIpCnEuJDMobixoLDIwNSkK
-bj1vLmEoci4kMigzLDIzNSkpCnEuJDMobixtLDExKQpxLiQzKG4saiw2OCkKcS4kMyhuLGwsMTgpCnEu
-JDMobixpLDE3MikKcS4kMyhuLGgsMjA1KQpuPW8uYShyLiQyKDQsMjI5KSkKcS4kMyhuLG0sNSkKcC4k
-MyhuLCJBWiIsMjI5KQpxLiQzKG4saywxMDIpCnEuJDMobiwiQCIsNjgpCnEuJDMobiwiWyIsMjMyKQpx
-LiQzKG4saiwxMzgpCnEuJDMobixpLDE3MikKcS4kMyhuLGgsMjA1KQpuPW8uYShyLiQyKDUsMjI5KSkK
-cS4kMyhuLG0sNSkKcC4kMyhuLCJBWiIsMjI5KQpxLiQzKG4saywxMDIpCnEuJDMobiwiQCIsNjgpCnEu
-JDMobixqLDEzOCkKcS4kMyhuLGksMTcyKQpxLiQzKG4saCwyMDUpCm49by5hKHIuJDIoNiwyMzEpKQpw
-LiQzKG4sIjE5Iiw3KQpxLiQzKG4sIkAiLDY4KQpxLiQzKG4saiwxMzgpCnEuJDMobixpLDE3MikKcS4k
-MyhuLGgsMjA1KQpuPW8uYShyLiQyKDcsMjMxKSkKcC4kMyhuLCIwOSIsNykKcS4kMyhuLCJAIiw2OCkK
-cS4kMyhuLGosMTM4KQpxLiQzKG4saSwxNzIpCnEuJDMobixoLDIwNSkKcS4kMyhvLmEoci4kMig4LDgp
-KSwiXSIsNSkKbj1vLmEoci4kMig5LDIzNSkpCnEuJDMobixtLDExKQpxLiQzKG4sbCwxNikKcS4kMyhu
-LGosMjM0KQpxLiQzKG4saSwxNzIpCnEuJDMobixoLDIwNSkKbj1vLmEoci4kMigxNiwyMzUpKQpxLiQz
-KG4sbSwxMSkKcS4kMyhuLGwsMTcpCnEuJDMobixqLDIzNCkKcS4kMyhuLGksMTcyKQpxLiQzKG4saCwy
-MDUpCm49by5hKHIuJDIoMTcsMjM1KSkKcS4kMyhuLG0sMTEpCnEuJDMobixqLDkpCnEuJDMobixpLDE3
-MikKcS4kMyhuLGgsMjA1KQpuPW8uYShyLiQyKDEwLDIzNSkpCnEuJDMobixtLDExKQpxLiQzKG4sbCwx
-OCkKcS4kMyhuLGosMjM0KQpxLiQzKG4saSwxNzIpCnEuJDMobixoLDIwNSkKbj1vLmEoci4kMigxOCwy
-MzUpKQpxLiQzKG4sbSwxMSkKcS4kMyhuLGwsMTkpCnEuJDMobixqLDIzNCkKcS4kMyhuLGksMTcyKQpx
-LiQzKG4saCwyMDUpCm49by5hKHIuJDIoMTksMjM1KSkKcS4kMyhuLG0sMTEpCnEuJDMobixqLDIzNCkK
-cS4kMyhuLGksMTcyKQpxLiQzKG4saCwyMDUpCm49by5hKHIuJDIoMTEsMjM1KSkKcS4kMyhuLG0sMTEp
-CnEuJDMobixqLDEwKQpxLiQzKG4saSwxNzIpCnEuJDMobixoLDIwNSkKbj1vLmEoci4kMigxMiwyMzYp
-KQpxLiQzKG4sbSwxMikKcS4kMyhuLGksMTIpCnEuJDMobixoLDIwNSkKbj1vLmEoci4kMigxMywyMzcp
-KQpxLiQzKG4sbSwxMykKcS4kMyhuLGksMTMpCnAuJDMoby5hKHIuJDIoMjAsMjQ1KSksImF6IiwyMSkK
-cj1vLmEoci4kMigyMSwyNDUpKQpwLiQzKHIsImF6IiwyMSkKcC4kMyhyLCIwOSIsMjEpCnEuJDMociwi
-Ky0uIiwyMSkKcmV0dXJuIGd9LApVQjpmdW5jdGlvbihhLGIsYyxkLGUpe3ZhciBzLHIscSxwLG8sbj0k
-LnZaKCkKZm9yKHM9Si5yWShhKSxyPWI7cjxjOysrcil7aWYoZDwwfHxkPj1uLmxlbmd0aClyZXR1cm4g
-SC5PSChuLGQpCnE9bltkXQpwPXMuVyhhLHIpXjk2Cm89cVtwPjk1PzMxOnBdCmQ9byYzMQpDLk5tLlk1
-KGUsbz4+PjUscil9cmV0dXJuIGR9LApSeDpmdW5jdGlvbihhKXtpZihhLmI9PT03JiZDLnhCLm5DKGEu
-YSwicGFja2FnZSIpJiZhLmM8PTApcmV0dXJuIFAuZkYoYS5hLGEuZSxhLmYpCnJldHVybi0xfSwKZkY6
-ZnVuY3Rpb24oYSxiLGMpe3ZhciBzLHIscQpmb3Iocz1iLHI9MDtzPGM7KytzKXtxPUMueEIuTyhhLHMp
-CmlmKHE9PT00NylyZXR1cm4gciE9PTA/czotMQppZihxPT09Mzd8fHE9PT01OClyZXR1cm4tMQpyfD1x
-XjQ2fXJldHVybi0xfSwKV0Y6ZnVuY3Rpb24gV0YoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sCmlQOmZ1
-bmN0aW9uIGlQKGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9LApYUzpmdW5jdGlvbiBYUygpe30sCkM2OmZ1
-bmN0aW9uIEM2KGEpe3RoaXMuYT1hfSwKRXo6ZnVuY3Rpb24gRXooKXt9LApMSzpmdW5jdGlvbiBMSygp
-e30sCmM6ZnVuY3Rpb24gYyhhLGIsYyxkKXt2YXIgXz10aGlzCl8uYT1hCl8uYj1iCl8uYz1jCl8uZD1k
-fSwKYko6ZnVuY3Rpb24gYkooYSxiLGMsZCxlLGYpe3ZhciBfPXRoaXMKXy5lPWEKXy5mPWIKXy5hPWMK
-Xy5iPWQKXy5jPWUKXy5kPWZ9LAplWTpmdW5jdGlvbiBlWShhLGIsYyxkLGUpe3ZhciBfPXRoaXMKXy5m
-PWEKXy5hPWIKXy5iPWMKXy5jPWQKXy5kPWV9LAptcDpmdW5jdGlvbiBtcChhLGIsYyxkKXt2YXIgXz10
-aGlzCl8uYT1hCl8uYj1iCl8uYz1jCl8uZD1kfSwKdWI6ZnVuY3Rpb24gdWIoYSl7dGhpcy5hPWF9LApk
-czpmdW5jdGlvbiBkcyhhKXt0aGlzLmE9YX0sCmxqOmZ1bmN0aW9uIGxqKGEpe3RoaXMuYT1hfSwKVVY6
-ZnVuY3Rpb24gVVYoYSl7dGhpcy5hPWF9LAprNTpmdW5jdGlvbiBrNSgpe30sCktZOmZ1bmN0aW9uIEtZ
-KCl7fSwKdDc6ZnVuY3Rpb24gdDcoYSl7dGhpcy5hPWF9LApDRDpmdW5jdGlvbiBDRChhKXt0aGlzLmE9
-YX0sCmFFOmZ1bmN0aW9uIGFFKGEsYixjKXt0aGlzLmE9YQp0aGlzLmI9Ygp0aGlzLmM9Y30sCmNYOmZ1
-bmN0aW9uIGNYKCl7fSwKQW46ZnVuY3Rpb24gQW4oKXt9LApOMzpmdW5jdGlvbiBOMyhhLGIsYyl7dGhp
-cy5hPWEKdGhpcy5iPWIKdGhpcy4kdGk9Y30sCmM4OmZ1bmN0aW9uIGM4KCl7fSwKTWg6ZnVuY3Rpb24g
-TWgoKXt9LApaZDpmdW5jdGlvbiBaZCgpe30sCk06ZnVuY3Rpb24gTShhKXt0aGlzLmE9YX0sCm4xOmZ1
-bmN0aW9uIG4xKGEpe3RoaXMuYT1hfSwKY1M6ZnVuY3Rpb24gY1MoYSl7dGhpcy5hPWF9LApWQzpmdW5j
-dGlvbiBWQyhhKXt0aGlzLmE9YX0sCkpUOmZ1bmN0aW9uIEpUKGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9
-LApEbjpmdW5jdGlvbiBEbihhLGIsYyxkLGUsZixnKXt2YXIgXz10aGlzCl8uYT1hCl8uYj1iCl8uYz1j
-Cl8uZD1kCl8uZT1lCl8uZj1mCl8ucj1nCl8uUT1fLno9Xy55PV8ueD0kfSwKUlo6ZnVuY3Rpb24gUloo
-KXt9LApNRTpmdW5jdGlvbiBNRShhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKeTU6ZnVuY3Rpb24geTUo
-YSl7dGhpcy5hPWF9LApQRTpmdW5jdGlvbiBQRShhLGIsYyl7dGhpcy5hPWEKdGhpcy5iPWIKdGhpcy5j
-PWN9LAp5STpmdW5jdGlvbiB5SShhKXt0aGlzLmE9YX0sCmM2OmZ1bmN0aW9uIGM2KCl7fSwKcWQ6ZnVu
-Y3Rpb24gcWQoKXt9LApVZjpmdW5jdGlvbiBVZihhLGIsYyxkLGUsZixnLGgpe3ZhciBfPXRoaXMKXy5h
-PWEKXy5iPWIKXy5jPWMKXy5kPWQKXy5lPWUKXy5mPWYKXy5yPWcKXy54PWgKXy55PW51bGx9LApxZTpm
-dW5jdGlvbiBxZShhLGIsYyxkLGUsZixnKXt2YXIgXz10aGlzCl8uYT1hCl8uYj1iCl8uYz1jCl8uZD1k
-Cl8uZT1lCl8uZj1mCl8ucj1nCl8uUT1fLno9Xy55PV8ueD0kfSwKaUo6ZnVuY3Rpb24gaUooKXt9LApF
-MjpmdW5jdGlvbiBFMihhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKamc6ZnVuY3Rpb24gamcoYSxiKXt0
-aGlzLmE9YQp0aGlzLmI9Yn0sCkJmOmZ1bmN0aW9uIEJmKGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9LApB
-czpmdW5jdGlvbiBBcygpe30sCkdFOmZ1bmN0aW9uIEdFKGEpe3RoaXMuYT1hfSwKTjc6ZnVuY3Rpb24g
-TjcoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sCnVROmZ1bmN0aW9uIHVRKCl7fSwKaEY6ZnVuY3Rpb24g
-aEYoKXt9LApSNDpmdW5jdGlvbihhLGIsYyxkKXt2YXIgcyxyLHEKSC55OChiKQp0LmouYShkKQppZihI
-Lm9UKGIpKXtzPVtjXQpDLk5tLkZWKHMsZCkKZD1zfXI9dC56CnE9UC5DSChKLk0xKGQsUC53MCgpLHIp
-LCEwLHIpCnQuWS5hKGEpCnJldHVybiBQLndZKEguRWsoYSxxLG51bGwpKX0sCkRtOmZ1bmN0aW9uKGEs
-YixjKXt2YXIgcwp0cnl7aWYoT2JqZWN0LmlzRXh0ZW5zaWJsZShhKSYmIU9iamVjdC5wcm90b3R5cGUu
-aGFzT3duUHJvcGVydHkuY2FsbChhLGIpKXtPYmplY3QuZGVmaW5lUHJvcGVydHkoYSxiLHt2YWx1ZTpj
-fSkKcmV0dXJuITB9fWNhdGNoKHMpe0guUnUocyl9cmV0dXJuITF9LApPbTpmdW5jdGlvbihhLGIpe2lm
-KE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChhLGIpKXJldHVybiBhW2JdCnJldHVy
-biBudWxsfSwKd1k6ZnVuY3Rpb24oYSl7aWYoYT09bnVsbHx8dHlwZW9mIGE9PSJzdHJpbmcifHx0eXBl
-b2YgYT09Im51bWJlciJ8fEguclEoYSkpcmV0dXJuIGEKaWYoYSBpbnN0YW5jZW9mIFAuRTQpcmV0dXJu
-IGEuYQppZihILlI5KGEpKXJldHVybiBhCmlmKHQuYWsuYihhKSlyZXR1cm4gYQppZihhIGluc3RhbmNl
-b2YgUC5pUClyZXR1cm4gSC5vMihhKQppZih0LlkuYihhKSlyZXR1cm4gUC5oRShhLCIkZGFydF9qc0Z1
-bmN0aW9uIixuZXcgUC5QQygpKQpyZXR1cm4gUC5oRShhLCJfJGRhcnRfanNPYmplY3QiLG5ldyBQLm10
-KCQua0koKSkpfSwKaEU6ZnVuY3Rpb24oYSxiLGMpe3ZhciBzPVAuT20oYSxiKQppZihzPT1udWxsKXtz
-PWMuJDEoYSkKUC5EbShhLGIscyl9cmV0dXJuIHN9LApkVTpmdW5jdGlvbihhKXt2YXIgcyxyCmlmKGE9
-PW51bGx8fHR5cGVvZiBhPT0ic3RyaW5nInx8dHlwZW9mIGE9PSJudW1iZXIifHx0eXBlb2YgYT09ImJv
-b2xlYW4iKXJldHVybiBhCmVsc2UgaWYoYSBpbnN0YW5jZW9mIE9iamVjdCYmSC5SOShhKSlyZXR1cm4g
-YQplbHNlIGlmKGEgaW5zdGFuY2VvZiBPYmplY3QmJnQuYWsuYihhKSlyZXR1cm4gYQplbHNlIGlmKGEg
-aW5zdGFuY2VvZiBEYXRlKXtzPUgudVAoYS5nZXRUaW1lKCkpCmlmKE1hdGguYWJzKHMpPD04NjRlMTMp
-cj0hMQplbHNlIHI9ITAKaWYocilILnYoUC54WSgiRGF0ZVRpbWUgaXMgb3V0c2lkZSB2YWxpZCByYW5n
-ZTogIitzKSkKSC5jYighMSwiaXNVdGMiLHQueSkKcmV0dXJuIG5ldyBQLmlQKHMsITEpfWVsc2UgaWYo
-YS5jb25zdHJ1Y3Rvcj09PSQua0koKSlyZXR1cm4gYS5vCmVsc2UgcmV0dXJuIFAuTkQoYSl9LApORDpm
-dW5jdGlvbihhKXtpZih0eXBlb2YgYT09ImZ1bmN0aW9uIilyZXR1cm4gUC5pUShhLCQueigpLG5ldyBQ
-LlFTKCkpCmlmKGEgaW5zdGFuY2VvZiBBcnJheSlyZXR1cm4gUC5pUShhLCQuUjgoKSxuZXcgUC5ucCgp
-KQpyZXR1cm4gUC5pUShhLCQuUjgoKSxuZXcgUC5VdCgpKX0sCmlROmZ1bmN0aW9uKGEsYixjKXt2YXIg
-cz1QLk9tKGEsYikKaWYocz09bnVsbHx8IShhIGluc3RhbmNlb2YgT2JqZWN0KSl7cz1jLiQxKGEpClAu
-RG0oYSxiLHMpfXJldHVybiBzfSwKUEM6ZnVuY3Rpb24gUEMoKXt9LAptdDpmdW5jdGlvbiBtdChhKXt0
-aGlzLmE9YX0sClFTOmZ1bmN0aW9uIFFTKCl7fSwKbnA6ZnVuY3Rpb24gbnAoKXt9LApVdDpmdW5jdGlv
-biBVdCgpe30sCkU0OmZ1bmN0aW9uIEU0KGEpe3RoaXMuYT1hfSwKcjc6ZnVuY3Rpb24gcjcoYSl7dGhp
-cy5hPWF9LApUejpmdW5jdGlvbiBUeihhLGIpe3RoaXMuYT1hCnRoaXMuJHRpPWJ9LAp2ZzpmdW5jdGlv
-biB2Zygpe30sCm5kOmZ1bmN0aW9uIG5kKCl7fSwKS2U6ZnVuY3Rpb24gS2UoYSl7dGhpcy5hPWF9LApo
-aTpmdW5jdGlvbiBoaSgpe319LFI9ewpuejpmdW5jdGlvbihhKXt2YXIgcz1ILnVQKGEucSgwLCJub2Rl
-SWQiKSkKcmV0dXJuIG5ldyBSLkxMKEMuTm0uSHQoQy5yayxuZXcgUi5NRChhKSkscyl9LApPWDpmdW5j
-dGlvbihhKXtzd2l0Y2goYSl7Y2FzZSBDLkFkOnJldHVybiJBZGQgLyo/Ki8gaGludCIKY2FzZSBDLm5l
-OnJldHVybiJBZGQgLyohKi8gaGludCIKY2FzZSBDLndWOnJldHVybiJSZW1vdmUgLyo/Ki8gaGludCIK
-Y2FzZSBDLmZSOnJldHVybiJSZW1vdmUgLyohKi8gaGludCIKY2FzZSBDLm15OnJldHVybiJDaGFuZ2Ug
-dG8gLyo/Ki8gaGludCIKY2FzZSBDLnJ4OnJldHVybiJDaGFuZ2UgdG8gLyohKi8gaGludCJ9cmV0dXJu
-IG51bGx9LApMTDpmdW5jdGlvbiBMTChhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKTUQ6ZnVuY3Rpb24g
-TUQoYSl7dGhpcy5hPWF9LApINzpmdW5jdGlvbiBINyhhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifX0sVD17
-bVE6ZnVuY3Rpb24gbVEoKXt9fSxVPXsKamY6ZnVuY3Rpb24oYSl7dmFyIHMscixxLHAKaWYoYT09bnVs
-bClzPW51bGwKZWxzZXtzPUguUUkoW10sdC5kNykKZm9yKHI9Si5JVCh0LlUuYShhKSk7ci5GKCk7KXtx
-PXIuZ2woKQpwPUouVTYocSkKcy5wdXNoKG5ldyBVLlNlKEguaChwLnEocSwiZGVzY3JpcHRpb24iKSks
-SC5oKHAucShxLCJocmVmIikpKSl9fXJldHVybiBzfSwKTmQ6ZnVuY3Rpb24oYSl7dmFyIHMscgppZihh
-PT1udWxsKXM9bnVsbAplbHNle3M9SC5RSShbXSx0LmFBKQpmb3Iocj1KLklUKHQuVS5hKGEpKTtyLkYo
-KTspcy5wdXNoKFUuTmYoci5nbCgpKSl9cmV0dXJuIHN9LApOZjpmdW5jdGlvbihhKXt2YXIgcz1KLlU2
-KGEpLHI9SC5oKHMucShhLCJkZXNjcmlwdGlvbiIpKSxxPUguUUkoW10sdC5hSikKZm9yKHM9Si5JVCh0
-LlUuYShzLnEoYSwiZW50cmllcyIpKSk7cy5GKCk7KXEucHVzaChVLlJqKHMuZ2woKSkpCnJldHVybiBu
-ZXcgVS55RChyLHEpfSwKUmo6ZnVuY3Rpb24oYSl7dmFyIHMscj1KLlU2KGEpLHE9SC5oKHIucShhLCJk
-ZXNjcmlwdGlvbiIpKSxwPUguaChyLnEoYSwiZnVuY3Rpb24iKSksbz1yLnEoYSwibGluayIpCmlmKG89
-PW51bGwpbz1udWxsCmVsc2V7cz1KLlU2KG8pCm89bmV3IFUuTWwoSC5oKHMucShvLCJocmVmIikpLEgu
-dVAocy5xKG8sImxpbmUiKSksSC5oKHMucShvLCJwYXRoIikpKX1yPXQuZksuYShyLnEoYSwiaGludEFj
-dGlvbnMiKSkKcj1yPT1udWxsP251bGw6Si5NMShyLG5ldyBVLmFOKCksdC5hWCkKcj1yPT1udWxsP251
-bGw6ci5icigwKQpyZXR1cm4gbmV3IFUud2IocSxwLG8scj09bnVsbD9DLmRuOnIpfSwKZDI6ZnVuY3Rp
-b24gZDIoYSxiLGMsZCxlLGYpe3ZhciBfPXRoaXMKXy5hPWEKXy5iPWIKXy5jPWMKXy5kPWQKXy5lPWUK
-Xy5mPWZ9LApTZTpmdW5jdGlvbiBTZShhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKTWw6ZnVuY3Rpb24g
-TWwoYSxiLGMpe3RoaXMuYT1hCnRoaXMuYj1iCnRoaXMuYz1jfSwKeUQ6ZnVuY3Rpb24geUQoYSxiKXt0
-aGlzLmE9YQp0aGlzLmI9Yn0sCndiOmZ1bmN0aW9uIHdiKGEsYixjLGQpe3ZhciBfPXRoaXMKXy5hPWEK
-Xy5iPWIKXy5jPWMKXy5kPWR9LAphTjpmdW5jdGlvbiBhTigpe30sCmIwOmZ1bmN0aW9uIGIwKCl7fX0s
-Vz17CngzOmZ1bmN0aW9uKCl7cmV0dXJuIHdpbmRvd30sClpyOmZ1bmN0aW9uKCl7cmV0dXJuIGRvY3Vt
-ZW50fSwKSjY6ZnVuY3Rpb24oYSl7dmFyIHM9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgiYSIpCmlmKGEh
-PW51bGwpQy54bi5zTFUocyxhKQpyZXR1cm4gc30sCkxqOmZ1bmN0aW9uKGEpe3JldHVybiBDU1MuZXNj
-YXBlKGEpfSwKVTk6ZnVuY3Rpb24oYSxiLGMpe3ZhciBzLHI9ZG9jdW1lbnQuYm9keQpyLnRvU3RyaW5n
-CnM9Qy5SWS5yNihyLGEsYixjKQpzLnRvU3RyaW5nCnI9dC5hYwpyPW5ldyBILlU1KG5ldyBXLmU3KHMp
-LHIuQygiYTIobEQuRSkiKS5hKG5ldyBXLkN2KCkpLHIuQygiVTU8bEQuRT4iKSkKcmV0dXJuIHQuaC5h
-KHIuZ3I4KHIpKX0sCnJTOmZ1bmN0aW9uKGEpe3ZhciBzLHIscT0iZWxlbWVudCB0YWcgdW5hdmFpbGFi
-bGUiCnRyeXtzPUouWUUoYSkKaWYodHlwZW9mIHMuZ25zKGEpPT0ic3RyaW5nIilxPXMuZ25zKGEpfWNh
-dGNoKHIpe0guUnUocil9cmV0dXJuIHF9LApDMDpmdW5jdGlvbihhLGIpe2E9YStiJjUzNjg3MDkxMQph
-PWErKChhJjUyNDI4Nyk8PDEwKSY1MzY4NzA5MTEKcmV0dXJuIGFeYT4+PjZ9LApyRTpmdW5jdGlvbihh
-LGIsYyxkKXt2YXIgcz1XLkMwKFcuQzAoVy5DMChXLkMwKDAsYSksYiksYyksZCkscj1zKygocyY2NzEw
-ODg2Myk8PDMpJjUzNjg3MDkxMQpyXj1yPj4+MTEKcmV0dXJuIHIrKChyJjE2MzgzKTw8MTUpJjUzNjg3
-MDkxMX0sClROOmZ1bmN0aW9uKGEsYil7dmFyIHMscixxPWEuY2xhc3NMaXN0CmZvcihzPWIubGVuZ3Ro
-LHI9MDtyPGIubGVuZ3RoO2IubGVuZ3RoPT09c3x8KDAsSC5saykoYiksKytyKXEuYWRkKGJbcl0pfSwK
-SkU6ZnVuY3Rpb24oYSxiLGMsZCxlKXt2YXIgcz1XLmFGKG5ldyBXLnZOKGMpLHQuQikKaWYocyE9bnVs
-bCYmITApSi5kWihhLGIscywhMSkKcmV0dXJuIG5ldyBXLnhDKGEsYixzLCExLGUuQygieEM8MD4iKSl9
-LApUdzpmdW5jdGlvbihhKXt2YXIgcz1XLko2KG51bGwpLHI9d2luZG93LmxvY2F0aW9uCnM9bmV3IFcu
-SlEobmV3IFcubWsocyxyKSkKcy5DWShhKQpyZXR1cm4gc30sCnFEOmZ1bmN0aW9uKGEsYixjLGQpe3Qu
-aC5hKGEpCkguaChiKQpILmgoYykKdC5jci5hKGQpCnJldHVybiEwfSwKblo6ZnVuY3Rpb24oYSxiLGMs
-ZCl7dmFyIHMscixxCnQuaC5hKGEpCkguaChiKQpILmgoYykKcz10LmNyLmEoZCkuYQpyPXMuYQpDLnhu
-LnNMVShyLGMpCnE9ci5ob3N0bmFtZQpzPXMuYgppZighKHE9PXMuaG9zdG5hbWUmJnIucG9ydD09cy5w
-b3J0JiZyLnByb3RvY29sPT1zLnByb3RvY29sKSlpZihxPT09IiIpaWYoci5wb3J0PT09IiIpe3M9ci5w
-cm90b2NvbApzPXM9PT0iOiJ8fHM9PT0iIn1lbHNlIHM9ITEKZWxzZSBzPSExCmVsc2Ugcz0hMApyZXR1
-cm4gc30sCkJsOmZ1bmN0aW9uKCl7dmFyIHM9dC5OLHI9UC50TShDLlF4LHMpLHE9dC5kMC5hKG5ldyBX
-LklBKCkpLHA9SC5RSShbIlRFTVBMQVRFIl0sdC5zKQpzPW5ldyBXLmN0KHIsUC5McyhzKSxQLkxzKHMp
-LFAuTHMocyksbnVsbCkKcy5DWShudWxsLG5ldyBILmxKKEMuUXgscSx0LmZqKSxwLG51bGwpCnJldHVy
-biBzfSwKcWM6ZnVuY3Rpb24oYSl7dmFyIHMKaWYoYT09bnVsbClyZXR1cm4gbnVsbAppZigicG9zdE1l
-c3NhZ2UiIGluIGEpe3M9Vy5QMShhKQppZih0LmFTLmIocykpcmV0dXJuIHMKcmV0dXJuIG51bGx9ZWxz
-ZSByZXR1cm4gdC5jaC5hKGEpfSwKUDE6ZnVuY3Rpb24oYSl7aWYoYT09PXdpbmRvdylyZXR1cm4gdC5j
-aS5hKGEpCmVsc2UgcmV0dXJuIG5ldyBXLmRXKCl9LAphRjpmdW5jdGlvbihhLGIpe3ZhciBzPSQuWDMK
-aWYocz09PUMuTlUpcmV0dXJuIGEKcmV0dXJuIHMuUHkoYSxiKX0sCnFFOmZ1bmN0aW9uIHFFKCl7fSwK
-R2g6ZnVuY3Rpb24gR2goKXt9LApmWTpmdW5jdGlvbiBmWSgpe30sCnJaOmZ1bmN0aW9uIHJaKCl7fSwK
-QXo6ZnVuY3Rpb24gQXooKXt9LApRUDpmdW5jdGlvbiBRUCgpe30sCm54OmZ1bmN0aW9uIG54KCl7fSwK
-b0o6ZnVuY3Rpb24gb0ooKXt9LAppZDpmdW5jdGlvbiBpZCgpe30sClFGOmZ1bmN0aW9uIFFGKCl7fSwK
-Tmg6ZnVuY3Rpb24gTmgoKXt9LAphZTpmdW5jdGlvbiBhZSgpe30sCklCOmZ1bmN0aW9uIElCKCl7fSwK
-TlE6ZnVuY3Rpb24gTlEoKXt9LAp3ejpmdW5jdGlvbiB3eihhLGIpe3RoaXMuYT1hCnRoaXMuJHRpPWJ9
-LApjdjpmdW5jdGlvbiBjdigpe30sCkN2OmZ1bmN0aW9uIEN2KCl7fSwKZWE6ZnVuY3Rpb24gZWEoKXt9
-LApEMDpmdW5jdGlvbiBEMCgpe30sCmhIOmZ1bmN0aW9uIGhIKCl7fSwKaDQ6ZnVuY3Rpb24gaDQoKXt9
-LApicjpmdW5jdGlvbiBicigpe30sClZiOmZ1bmN0aW9uIFZiKCl7fSwKZko6ZnVuY3Rpb24gZkooKXt9
-LAp3YTpmdW5jdGlvbiB3YSgpe30sClNnOmZ1bmN0aW9uIFNnKCl7fSwKdzc6ZnVuY3Rpb24gdzcoKXt9
-LApBajpmdW5jdGlvbiBBaigpe30sCmU3OmZ1bmN0aW9uIGU3KGEpe3RoaXMuYT1hfSwKdUg6ZnVuY3Rp
-b24gdUgoKXt9LApCSDpmdW5jdGlvbiBCSCgpe30sClNOOmZ1bmN0aW9uIFNOKCl7fSwKZXc6ZnVuY3Rp
-b24gZXcoKXt9LApscDpmdW5jdGlvbiBscCgpe30sClRiOmZ1bmN0aW9uIFRiKCl7fSwKSXY6ZnVuY3Rp
-b24gSXYoKXt9LApXUDpmdW5jdGlvbiBXUCgpe30sCnlZOmZ1bmN0aW9uIHlZKCl7fSwKdzY6ZnVuY3Rp
-b24gdzYoKXt9LApLNTpmdW5jdGlvbiBLNSgpe30sCkNtOmZ1bmN0aW9uIENtKCl7fSwKQ1E6ZnVuY3Rp
-b24gQ1EoKXt9LAp3NDpmdW5jdGlvbiB3NCgpe30sCnJoOmZ1bmN0aW9uIHJoKCl7fSwKY2Y6ZnVuY3Rp
-b24gY2YoKXt9LAppNzpmdW5jdGlvbiBpNyhhKXt0aGlzLmE9YX0sClN5OmZ1bmN0aW9uIFN5KGEpe3Ro
-aXMuYT1hfSwKS1M6ZnVuY3Rpb24gS1MoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sCkEzOmZ1bmN0aW9u
-IEEzKGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9LApJNDpmdW5jdGlvbiBJNChhKXt0aGlzLmE9YX0sCkZr
-OmZ1bmN0aW9uIEZrKGEsYil7dGhpcy5hPWEKdGhpcy4kdGk9Yn0sClJPOmZ1bmN0aW9uIFJPKGEsYixj
-LGQpe3ZhciBfPXRoaXMKXy5hPWEKXy5iPWIKXy5jPWMKXy4kdGk9ZH0sCmV1OmZ1bmN0aW9uIGV1KGEs
-YixjLGQpe3ZhciBfPXRoaXMKXy5hPWEKXy5iPWIKXy5jPWMKXy4kdGk9ZH0sCnhDOmZ1bmN0aW9uIHhD
-KGEsYixjLGQsZSl7dmFyIF89dGhpcwpfLmI9YQpfLmM9YgpfLmQ9YwpfLmU9ZApfLiR0aT1lfSwKdk46
-ZnVuY3Rpb24gdk4oYSl7dGhpcy5hPWF9LApKUTpmdW5jdGlvbiBKUShhKXt0aGlzLmE9YX0sCkdtOmZ1
-bmN0aW9uIEdtKCl7fSwKdkQ6ZnVuY3Rpb24gdkQoYSl7dGhpcy5hPWF9LApVdjpmdW5jdGlvbiBVdihh
-KXt0aGlzLmE9YX0sCkVnOmZ1bmN0aW9uIEVnKGEsYixjKXt0aGlzLmE9YQp0aGlzLmI9Ygp0aGlzLmM9
-Y30sCm02OmZ1bmN0aW9uIG02KCl7fSwKRW86ZnVuY3Rpb24gRW8oKXt9LApXazpmdW5jdGlvbiBXaygp
-e30sCmN0OmZ1bmN0aW9uIGN0KGEsYixjLGQsZSl7dmFyIF89dGhpcwpfLmU9YQpfLmE9YgpfLmI9Ywpf
-LmM9ZApfLmQ9ZX0sCklBOmZ1bmN0aW9uIElBKCl7fSwKT3c6ZnVuY3Rpb24gT3coKXt9LApXOTpmdW5j
-dGlvbiBXOShhLGIsYyl7dmFyIF89dGhpcwpfLmE9YQpfLmI9YgpfLmM9LTEKXy5kPW51bGwKXy4kdGk9
-Y30sCmRXOmZ1bmN0aW9uIGRXKCl7fSwKbWs6ZnVuY3Rpb24gbWsoYSxiKXt0aGlzLmE9YQp0aGlzLmI9
-Yn0sCktvOmZ1bmN0aW9uIEtvKGEpe3RoaXMuYT1hCnRoaXMuYj0wfSwKZm06ZnVuY3Rpb24gZm0oYSl7
-dGhpcy5hPWF9LApMZTpmdW5jdGlvbiBMZSgpe30sCks3OmZ1bmN0aW9uIEs3KCl7fSwKckI6ZnVuY3Rp
-b24gckIoKXt9LApYVzpmdW5jdGlvbiBYVygpe30sCm9hOmZ1bmN0aW9uIG9hKCl7fX0sWD17CkNMOmZ1
-bmN0aW9uKGEsYil7dmFyIHMscixxLHAsbyxuPWIueFooYSkKYi5oSyhhKQppZihuIT1udWxsKWE9Si5L
-VihhLG4ubGVuZ3RoKQpzPXQucwpyPUguUUkoW10scykKcT1ILlFJKFtdLHMpCnM9YS5sZW5ndGgKaWYo
-cyE9PTAmJmIucjQoQy54Qi5XKGEsMCkpKXtpZigwPj1zKXJldHVybiBILk9IKGEsMCkKQy5ObS5pKHEs
-YVswXSkKcD0xfWVsc2V7Qy5ObS5pKHEsIiIpCnA9MH1mb3Iobz1wO288czsrK28paWYoYi5yNChDLnhC
-LlcoYSxvKSkpe0MuTm0uaShyLEMueEIuTmooYSxwLG8pKQpDLk5tLmkocSxhW29dKQpwPW8rMX1pZihw
-PHMpe0MuTm0uaShyLEMueEIueW4oYSxwKSkKQy5ObS5pKHEsIiIpfXJldHVybiBuZXcgWC5XRChiLG4s
-cixxKX0sCldEOmZ1bmN0aW9uIFdEKGEsYixjLGQpe3ZhciBfPXRoaXMKXy5hPWEKXy5iPWIKXy5kPWMK
-Xy5lPWR9LApJNzpmdW5jdGlvbihhKXtyZXR1cm4gbmV3IFguZHYoYSl9LApkdjpmdW5jdGlvbiBkdihh
-KXt0aGlzLmE9YX19CnZhciB3PVtCLEMsRCxFLEYsSCxKLEwsTSxPLFAsUixULFUsVyxYXQpodW5rSGVs
-cGVycy5zZXRGdW5jdGlvbk5hbWVzSWZOZWNlc3NhcnkodykKdmFyICQ9e30KSC5GSy5wcm90b3R5cGU9
-e30KSi5Hdi5wcm90b3R5cGU9ewpETjpmdW5jdGlvbihhLGIpe3JldHVybiBhPT09Yn0sCmdpTzpmdW5j
-dGlvbihhKXtyZXR1cm4gSC5lUShhKX0sCnc6ZnVuY3Rpb24oYSl7cmV0dXJuIkluc3RhbmNlIG9mICci
-K0guRWooSC5saChhKSkrIicifSwKZTc6ZnVuY3Rpb24oYSxiKXt0Lm8uYShiKQp0aHJvdyBILmIoUC5s
-cihhLGIuZ1dhKCksYi5nbmQoKSxiLmdWbSgpKSl9fQpKLnlFLnByb3RvdHlwZT17Cnc6ZnVuY3Rpb24o
-YSl7cmV0dXJuIFN0cmluZyhhKX0sCmdpTzpmdW5jdGlvbihhKXtyZXR1cm4gYT81MTkwMTg6MjE4MTU5
-fSwKJGlhMjoxfQpKLndlLnByb3RvdHlwZT17CkROOmZ1bmN0aW9uKGEsYil7cmV0dXJuIG51bGw9PWJ9
-LAp3OmZ1bmN0aW9uKGEpe3JldHVybiJudWxsIn0sCmdpTzpmdW5jdGlvbihhKXtyZXR1cm4gMH0sCmU3
-OmZ1bmN0aW9uKGEsYil7cmV0dXJuIHRoaXMuU2ooYSx0Lm8uYShiKSl9LAokaWM4OjF9CkouTUYucHJv
-dG90eXBlPXsKZ2lPOmZ1bmN0aW9uKGEpe3JldHVybiAwfSwKdzpmdW5jdGlvbihhKXtyZXR1cm4gU3Ry
-aW5nKGEpfSwKJGl2bToxfQpKLmlDLnByb3RvdHlwZT17fQpKLmtkLnByb3RvdHlwZT17fQpKLmM1LnBy
-b3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7dmFyIHM9YVskLnooKV0KaWYocz09bnVsbClyZXR1cm4gdGhp
-cy50KGEpCnJldHVybiJKYXZhU2NyaXB0IGZ1bmN0aW9uIGZvciAiK0guRWooSi53KHMpKX0sCiRpRUg6
-MX0KSi5qZC5wcm90b3R5cGU9ewpkcjpmdW5jdGlvbihhLGIpe3JldHVybiBuZXcgSC5qVihhLEgudDYo
-YSkuQygiQDwxPiIpLktxKGIpLkMoImpWPDEsMj4iKSl9LAppOmZ1bmN0aW9uKGEsYil7SC50NihhKS5j
-LmEoYikKaWYoISFhLmZpeGVkJGxlbmd0aClILnYoUC5MNCgiYWRkIikpCmEucHVzaChiKX0sClc0OmZ1
-bmN0aW9uKGEsYil7dmFyIHMKaWYoISFhLmZpeGVkJGxlbmd0aClILnYoUC5MNCgicmVtb3ZlQXQiKSkK
-cz1hLmxlbmd0aAppZihiPj1zKXRocm93IEguYihQLk83KGIsbnVsbCkpCnJldHVybiBhLnNwbGljZShi
-LDEpWzBdfSwKVUc6ZnVuY3Rpb24oYSxiLGMpe3ZhciBzLHIKSC50NihhKS5DKCJjWDwxPiIpLmEoYykK
-aWYoISFhLmZpeGVkJGxlbmd0aClILnYoUC5MNCgiaW5zZXJ0QWxsIikpClAud0EoYiwwLGEubGVuZ3Ro
-LCJpbmRleCIpCmlmKCF0LmQuYihjKSljPUouUlgoYykKcz1KLkhtKGMpCmEubGVuZ3RoPWEubGVuZ3Ro
-K3MKcj1iK3MKdGhpcy5ZVyhhLHIsYS5sZW5ndGgsYSxiKQp0aGlzLnZnKGEsYixyLGMpfSwKRlY6ZnVu
-Y3Rpb24oYSxiKXt2YXIgcwpILnQ2KGEpLkMoImNYPDE+IikuYShiKQppZighIWEuZml4ZWQkbGVuZ3Ro
-KUgudihQLkw0KCJhZGRBbGwiKSkKaWYoQXJyYXkuaXNBcnJheShiKSl7dGhpcy5LaChhLGIpCnJldHVy
-bn1mb3Iocz1KLklUKGIpO3MuRigpOylhLnB1c2gocy5nbCgpKX0sCktoOmZ1bmN0aW9uKGEsYil7dmFy
-IHMscgp0LmIuYShiKQpzPWIubGVuZ3RoCmlmKHM9PT0wKXJldHVybgppZihhPT09Yil0aHJvdyBILmIo
-UC5hNChhKSkKZm9yKHI9MDtyPHM7KytyKWEucHVzaChiW3JdKX0sCkUyOmZ1bmN0aW9uKGEsYixjKXt2
-YXIgcz1ILnQ2KGEpCnJldHVybiBuZXcgSC5sSihhLHMuS3EoYykuQygiMSgyKSIpLmEoYikscy5DKCJA
-PDE+IikuS3EoYykuQygibEo8MSwyPiIpKX0sCms6ZnVuY3Rpb24oYSxiKXt2YXIgcyxyPVAuTzgoYS5s
-ZW5ndGgsIiIsITEsdC5OKQpmb3Iocz0wO3M8YS5sZW5ndGg7KytzKXRoaXMuWTUocixzLEguRWooYVtz
-XSkpCnJldHVybiByLmpvaW4oYil9LAplUjpmdW5jdGlvbihhLGIpe3JldHVybiBILnFDKGEsYixudWxs
-LEgudDYoYSkuYyl9LApOMDpmdW5jdGlvbihhLGIsYyxkKXt2YXIgcyxyLHEKZC5hKGIpCkgudDYoYSku
-S3EoZCkuQygiMSgxLDIpIikuYShjKQpzPWEubGVuZ3RoCmZvcihyPWIscT0wO3E8czsrK3Epe3I9Yy4k
-MihyLGFbcV0pCmlmKGEubGVuZ3RoIT09cyl0aHJvdyBILmIoUC5hNChhKSl9cmV0dXJuIHJ9LApIdDpm
-dW5jdGlvbihhLGIpe3ZhciBzLHIscSxwLG8KSC50NihhKS5DKCJhMigxKSIpLmEoYikKcz1hLmxlbmd0
-aApmb3Iocj1udWxsLHE9ITEscD0wO3A8czsrK3Ape289YVtwXQppZihILm9UKGIuJDEobykpKXtpZihx
-KXRocm93IEguYihILkFtKCkpCnI9bwpxPSEwfWlmKHMhPT1hLmxlbmd0aCl0aHJvdyBILmIoUC5hNChh
-KSl9aWYocSlyZXR1cm4gcgp0aHJvdyBILmIoSC5XcCgpKX0sCkU6ZnVuY3Rpb24oYSxiKXtpZihiPDB8
-fGI+PWEubGVuZ3RoKXJldHVybiBILk9IKGEsYikKcmV0dXJuIGFbYl19LApndEg6ZnVuY3Rpb24oYSl7
-aWYoYS5sZW5ndGg+MClyZXR1cm4gYVswXQp0aHJvdyBILmIoSC5XcCgpKX0sCmdyWjpmdW5jdGlvbihh
-KXt2YXIgcz1hLmxlbmd0aAppZihzPjApcmV0dXJuIGFbcy0xXQp0aHJvdyBILmIoSC5XcCgpKX0sCllX
-OmZ1bmN0aW9uKGEsYixjLGQsZSl7dmFyIHMscixxLHAsbwpILnQ2KGEpLkMoImNYPDE+IikuYShkKQpp
-ZighIWEuaW1tdXRhYmxlJGxpc3QpSC52KFAuTDQoInNldFJhbmdlIikpClAuakIoYixjLGEubGVuZ3Ro
-KQpzPWMtYgppZihzPT09MClyZXR1cm4KUC5rMShlLCJza2lwQ291bnQiKQppZih0LmouYihkKSl7cj1k
-CnE9ZX1lbHNle3I9Si5BNShkLGUpLnR0KDAsITEpCnE9MH1wPUouVTYocikKaWYocStzPnAuZ0Eocikp
-dGhyb3cgSC5iKEguYXIoKSkKaWYocTxiKWZvcihvPXMtMTtvPj0wOy0tbylhW2Irb109cC5xKHIscStv
-KQplbHNlIGZvcihvPTA7bzxzOysrbylhW2Irb109cC5xKHIscStvKX0sCnZnOmZ1bmN0aW9uKGEsYixj
-LGQpe3JldHVybiB0aGlzLllXKGEsYixjLGQsMCl9LApWcjpmdW5jdGlvbihhLGIpe3ZhciBzLHIKSC50
-NihhKS5DKCJhMigxKSIpLmEoYikKcz1hLmxlbmd0aApmb3Iocj0wO3I8czsrK3Ipe2lmKEgub1QoYi4k
-MShhW3JdKSkpcmV0dXJuITAKaWYoYS5sZW5ndGghPT1zKXRocm93IEguYihQLmE0KGEpKX1yZXR1cm4h
-MX0sCnRnOmZ1bmN0aW9uKGEsYil7dmFyIHMKZm9yKHM9MDtzPGEubGVuZ3RoOysrcylpZihKLlJNKGFb
-c10sYikpcmV0dXJuITAKcmV0dXJuITF9LApnbDA6ZnVuY3Rpb24oYSl7cmV0dXJuIGEubGVuZ3RoPT09
-MH0sCmdvcjpmdW5jdGlvbihhKXtyZXR1cm4gYS5sZW5ndGghPT0wfSwKdzpmdW5jdGlvbihhKXtyZXR1
-cm4gUC54KGEsIlsiLCJdIil9LAp0dDpmdW5jdGlvbihhLGIpe3ZhciBzPUguUUkoYS5zbGljZSgwKSxI
-LnQ2KGEpKQpyZXR1cm4gc30sCmJyOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLnR0KGEsITApfSwKZ206
-ZnVuY3Rpb24oYSl7cmV0dXJuIG5ldyBKLm0xKGEsYS5sZW5ndGgsSC50NihhKS5DKCJtMTwxPiIpKX0s
-CmdpTzpmdW5jdGlvbihhKXtyZXR1cm4gSC5lUShhKX0sCmdBOmZ1bmN0aW9uKGEpe3JldHVybiBhLmxl
-bmd0aH0sCnNBOmZ1bmN0aW9uKGEsYil7aWYoISFhLmZpeGVkJGxlbmd0aClILnYoUC5MNCgic2V0IGxl
-bmd0aCIpKQppZihiPDApdGhyb3cgSC5iKFAuVEUoYiwwLG51bGwsIm5ld0xlbmd0aCIsbnVsbCkpCmEu
-bGVuZ3RoPWJ9LApxOmZ1bmN0aW9uKGEsYil7SC51UChiKQppZihiPj1hLmxlbmd0aHx8YjwwKXRocm93
-IEguYihILnUoYSxiKSkKcmV0dXJuIGFbYl19LApZNTpmdW5jdGlvbihhLGIsYyl7SC50NihhKS5jLmEo
-YykKaWYoISFhLmltbXV0YWJsZSRsaXN0KUgudihQLkw0KCJpbmRleGVkIHNldCIpKQppZihiPj1hLmxl
-bmd0aHx8YjwwKXRocm93IEguYihILnUoYSxiKSkKYVtiXT1jfSwKJGliUToxLAokaWNYOjEsCiRpek06
-MX0KSi5Qby5wcm90b3R5cGU9e30KSi5tMS5wcm90b3R5cGU9ewpnbDpmdW5jdGlvbigpe3JldHVybiB0
-aGlzLmR9LApGOmZ1bmN0aW9uKCl7dmFyIHMscj10aGlzLHE9ci5hLHA9cS5sZW5ndGgKaWYoci5iIT09
-cCl0aHJvdyBILmIoSC5sayhxKSkKcz1yLmMKaWYocz49cCl7ci5zTShudWxsKQpyZXR1cm4hMX1yLnNN
-KHFbc10pOysrci5jCnJldHVybiEwfSwKc006ZnVuY3Rpb24oYSl7dGhpcy5kPXRoaXMuJHRpLkMoIjE/
-IikuYShhKX0sCiRpQW46MX0KSi5xSS5wcm90b3R5cGU9ewp6UTpmdW5jdGlvbihhKXtpZihhPjApe2lm
-KGEhPT0xLzApcmV0dXJuIE1hdGgucm91bmQoYSl9ZWxzZSBpZihhPi0xLzApcmV0dXJuIDAtTWF0aC5y
-b3VuZCgwLWEpCnRocm93IEguYihQLkw0KCIiK2ErIi5yb3VuZCgpIikpfSwKdzpmdW5jdGlvbihhKXtp
-ZihhPT09MCYmMS9hPDApcmV0dXJuIi0wLjAiCmVsc2UgcmV0dXJuIiIrYX0sCmdpTzpmdW5jdGlvbihh
-KXt2YXIgcyxyLHEscCxvPWF8MAppZihhPT09bylyZXR1cm4gbyY1MzY4NzA5MTEKcz1NYXRoLmFicyhh
-KQpyPU1hdGgubG9nKHMpLzAuNjkzMTQ3MTgwNTU5OTQ1M3wwCnE9TWF0aC5wb3coMixyKQpwPXM8MT9z
-L3E6cS9zCnJldHVybigocCo5MDA3MTk5MjU0NzQwOTkyfDApKyhwKjM1NDIyNDMxODExNzY1MjF8MCkp
-KjU5OTE5NytyKjEyNTkmNTM2ODcwOTExfSwKelk6ZnVuY3Rpb24oYSxiKXt2YXIgcz1hJWIKaWYocz09
-PTApcmV0dXJuIDAKaWYocz4wKXJldHVybiBzCnJldHVybiBzK2J9LApCVTpmdW5jdGlvbihhLGIpe3Jl
-dHVybihhfDApPT09YT9hL2J8MDp0aGlzLkRKKGEsYil9LApESjpmdW5jdGlvbihhLGIpe3ZhciBzPWEv
-YgppZihzPj0tMjE0NzQ4MzY0OCYmczw9MjE0NzQ4MzY0NylyZXR1cm4gc3wwCmlmKHM+MCl7aWYocyE9
-PTEvMClyZXR1cm4gTWF0aC5mbG9vcihzKX1lbHNlIGlmKHM+LTEvMClyZXR1cm4gTWF0aC5jZWlsKHMp
-CnRocm93IEguYihQLkw0KCJSZXN1bHQgb2YgdHJ1bmNhdGluZyBkaXZpc2lvbiBpcyAiK0guRWoocykr
-IjogIitILkVqKGEpKyIgfi8gIitiKSl9LAp3RzpmdW5jdGlvbihhLGIpe3ZhciBzCmlmKGE+MClzPXRo
-aXMucDMoYSxiKQplbHNle3M9Yj4zMT8zMTpiCnM9YT4+cz4+PjB9cmV0dXJuIHN9LApiZjpmdW5jdGlv
-bihhLGIpe2lmKDA+Yil0aHJvdyBILmIoSC50TChiKSkKcmV0dXJuIHRoaXMucDMoYSxiKX0sCnAzOmZ1
-bmN0aW9uKGEsYil7cmV0dXJuIGI+MzE/MDphPj4+Yn0sCiRpQ1A6MSwKJGlaWjoxfQpKLmJVLnByb3Rv
-dHlwZT17JGlJajoxfQpKLmtELnByb3RvdHlwZT17fQpKLkRyLnByb3RvdHlwZT17Ck86ZnVuY3Rpb24o
-YSxiKXtpZihiPDApdGhyb3cgSC5iKEgudShhLGIpKQppZihiPj1hLmxlbmd0aClILnYoSC51KGEsYikp
-CnJldHVybiBhLmNoYXJDb2RlQXQoYil9LApXOmZ1bmN0aW9uKGEsYil7aWYoYj49YS5sZW5ndGgpdGhy
-b3cgSC5iKEgudShhLGIpKQpyZXR1cm4gYS5jaGFyQ29kZUF0KGIpfSwKZGQ6ZnVuY3Rpb24oYSxiKXty
-ZXR1cm4gbmV3IEgudW4oYixhLDApfSwKaDpmdW5jdGlvbihhLGIpe2lmKHR5cGVvZiBiIT0ic3RyaW5n
-Iil0aHJvdyBILmIoUC5MMyhiLG51bGwsbnVsbCkpCnJldHVybiBhK2J9LApUYzpmdW5jdGlvbihhLGIp
-e3ZhciBzPWIubGVuZ3RoLHI9YS5sZW5ndGgKaWYocz5yKXJldHVybiExCnJldHVybiBiPT09dGhpcy55
-bihhLHItcyl9LAppNzpmdW5jdGlvbihhLGIsYyxkKXt2YXIgcz1QLmpCKGIsYyxhLmxlbmd0aCkscj1h
-LnN1YnN0cmluZygwLGIpLHE9YS5zdWJzdHJpbmcocykKcmV0dXJuIHIrZCtxfSwKUWk6ZnVuY3Rpb24o
-YSxiLGMpe3ZhciBzCmlmKGM8MHx8Yz5hLmxlbmd0aCl0aHJvdyBILmIoUC5URShjLDAsYS5sZW5ndGgs
-bnVsbCxudWxsKSkKcz1jK2IubGVuZ3RoCmlmKHM+YS5sZW5ndGgpcmV0dXJuITEKcmV0dXJuIGI9PT1h
-LnN1YnN0cmluZyhjLHMpfSwKbkM6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gdGhpcy5RaShhLGIsMCl9LApO
+PW8rMQppZihpPG4pe2E9SC5IcChDLnhCLk5qKGE1LGksbiksYTMpCmEwPVAud0IoYT09bnVsbD9ILnYo
+UC5ycigiSW52YWxpZCBwb3J0IixhNSxpKSk6YSxqKX1lbHNlIGEwPWEzfWVsc2V7YTA9YTMKYj1hMApj
+PSIifWExPVAua2EoYTUsbixtLGEzLGosYiE9bnVsbCkKYTI9bTxsP1AubGUoYTUsbSsxLGwsYTMpOmEz
+CnJldHVybiBuZXcgUC5EbihqLGMsYixhMCxhMSxhMixsPGE0P1AudEcoYTUsbCsxLGE0KTphMyl9LApN
+dDpmdW5jdGlvbihhKXtILm4oYSkKcmV0dXJuIFAua3UoYSwwLGEubGVuZ3RoLEMueE0sITEpfSwKV1g6
+ZnVuY3Rpb24oYSl7dmFyIHM9dC5OCnJldHVybiBDLk5tLk4wKEguUUkoYS5zcGxpdCgiJiIpLHQucyks
+UC5GbChzLHMpLG5ldyBQLm4xKEMueE0pLHQudil9LApIaDpmdW5jdGlvbihhLGIsYyl7dmFyIHMscixx
+LHAsbyxuLG09IklQdjQgYWRkcmVzcyBzaG91bGQgY29udGFpbiBleGFjdGx5IDQgcGFydHMiLGw9ImVh
+Y2ggcGFydCBtdXN0IGJlIGluIHRoZSByYW5nZSAwLi4yNTUiLGs9bmV3IFAuY1MoYSksaj1uZXcgVWlu
+dDhBcnJheSg0KQpmb3Iocz1iLHI9cyxxPTA7czxjOysrcyl7cD1DLnhCLk8oYSxzKQppZihwIT09NDYp
+e2lmKChwXjQ4KT45KWsuJDIoImludmFsaWQgY2hhcmFjdGVyIixzKX1lbHNle2lmKHE9PT0zKWsuJDIo
+bSxzKQpvPVAuUUEoQy54Qi5OaihhLHIscyksbnVsbCkKaWYobz4yNTUpay4kMihsLHIpCm49cSsxCmlm
+KHE+PTQpcmV0dXJuIEguT0goaixxKQpqW3FdPW8Kcj1zKzEKcT1ufX1pZihxIT09MylrLiQyKG0sYykK
+bz1QLlFBKEMueEIuTmooYSxyLGMpLG51bGwpCmlmKG8+MjU1KWsuJDIobCxyKQppZihxPj00KXJldHVy
+biBILk9IKGoscSkKaltxXT1vCnJldHVybiBqfSwKZWc6ZnVuY3Rpb24oYSxiLGEwKXt2YXIgcyxyLHEs
+cCxvLG4sbSxsLGssaixpLGgsZyxmLGUsZD1uZXcgUC5WQyhhKSxjPW5ldyBQLkpUKGQsYSkKaWYoYS5s
+ZW5ndGg8MilkLiQxKCJhZGRyZXNzIGlzIHRvbyBzaG9ydCIpCnM9SC5RSShbXSx0LnQpCmZvcihyPWIs
+cT1yLHA9ITEsbz0hMTtyPGEwOysrcil7bj1DLnhCLk8oYSxyKQppZihuPT09NTgpe2lmKHI9PT1iKXsr
+K3IKaWYoQy54Qi5PKGEscikhPT01OClkLiQyKCJpbnZhbGlkIHN0YXJ0IGNvbG9uLiIscikKcT1yfWlm
+KHI9PT1xKXtpZihwKWQuJDIoIm9ubHkgb25lIHdpbGRjYXJkIGA6OmAgaXMgYWxsb3dlZCIscikKQy5O
+bS5pKHMsLTEpCnA9ITB9ZWxzZSBDLk5tLmkocyxjLiQyKHEscikpCnE9cisxfWVsc2UgaWYobj09PTQ2
+KW89ITB9aWYocy5sZW5ndGg9PT0wKWQuJDEoInRvbyBmZXcgcGFydHMiKQptPXE9PT1hMApsPUMuTm0u
+Z3JaKHMpCmlmKG0mJmwhPT0tMSlkLiQyKCJleHBlY3RlZCBhIHBhcnQgYWZ0ZXIgbGFzdCBgOmAiLGEw
+KQppZighbSlpZighbylDLk5tLmkocyxjLiQyKHEsYTApKQplbHNle2s9UC5IaChhLHEsYTApCkMuTm0u
+aShzLChrWzBdPDw4fGtbMV0pPj4+MCkKQy5ObS5pKHMsKGtbMl08PDh8a1szXSk+Pj4wKX1pZihwKXtp
+ZihzLmxlbmd0aD43KWQuJDEoImFuIGFkZHJlc3Mgd2l0aCBhIHdpbGRjYXJkIG11c3QgaGF2ZSBsZXNz
+IHRoYW4gNyBwYXJ0cyIpfWVsc2UgaWYocy5sZW5ndGghPT04KWQuJDEoImFuIGFkZHJlc3Mgd2l0aG91
+dCBhIHdpbGRjYXJkIG11c3QgY29udGFpbiBleGFjdGx5IDggcGFydHMiKQpqPW5ldyBVaW50OEFycmF5
+KDE2KQpmb3IobD1zLmxlbmd0aCxpPTktbCxyPTAsaD0wO3I8bDsrK3Ipe2c9c1tyXQppZihnPT09LTEp
+Zm9yKGY9MDtmPGk7KytmKXtpZihoPDB8fGg+PTE2KXJldHVybiBILk9IKGosaCkKaltoXT0wCmU9aCsx
+CmlmKGU+PTE2KXJldHVybiBILk9IKGosZSkKaltlXT0wCmgrPTJ9ZWxzZXtlPUMuam4ud0coZyw4KQpp
+ZihoPDB8fGg+PTE2KXJldHVybiBILk9IKGosaCkKaltoXT1lCmU9aCsxCmlmKGU+PTE2KXJldHVybiBI
+Lk9IKGosZSkKaltlXT1nJjI1NQpoKz0yfX1yZXR1cm4gan0sCktMOmZ1bmN0aW9uKGEsYixjLGQsZSxm
+LGcpe3ZhciBzLHIscSxwLG8sbgpmPWY9PW51bGw/IiI6UC5QaShmLDAsZi5sZW5ndGgpCmc9UC56Uihn
+LDAsZz09bnVsbD8wOmcubGVuZ3RoKQphPVAuT2UoYSwwLGE9PW51bGw/MDphLmxlbmd0aCwhMSkKcz1Q
+LmxlKG51bGwsMCwwLGUpCnI9UC50RyhudWxsLDAsMCkKZD1QLndCKGQsZikKcT1mPT09ImZpbGUiCmlm
+KGE9PW51bGwpcD1nLmxlbmd0aCE9PTB8fGQhPW51bGx8fHEKZWxzZSBwPSExCmlmKHApYT0iIgpwPWE9
+PW51bGwKbz0hcApiPVAua2EoYiwwLGI9PW51bGw/MDpiLmxlbmd0aCxjLGYsbykKbj1mLmxlbmd0aD09
+PTAKaWYobiYmcCYmIUMueEIubihiLCIvIikpYj1QLndGKGIsIW58fG8pCmVsc2UgYj1QLnhlKGIpCnJl
+dHVybiBuZXcgUC5EbihmLGcscCYmQy54Qi5uKGIsIi8vIik/IiI6YSxkLGIscyxyKX0sCndLOmZ1bmN0
+aW9uKGEpe2lmKGE9PT0iaHR0cCIpcmV0dXJuIDgwCmlmKGE9PT0iaHR0cHMiKXJldHVybiA0NDMKcmV0
+dXJuIDB9LApOUjpmdW5jdGlvbihhLGIpe3ZhciBzLHIscSxwLG8sbgpmb3Iocz1hLmxlbmd0aCxyPTA7
+cjxzOysrcil7cT1DLnhCLlcoYSxyKQpwPUMueEIuVyhiLHIpCm89cV5wCmlmKG8hPT0wKXtpZihvPT09
+MzIpe249cHxvCmlmKDk3PD1uJiZuPD0xMjIpY29udGludWV9cmV0dXJuITF9fXJldHVybiEwfSwKUjM6
+ZnVuY3Rpb24oYSxiLGMpe3Rocm93IEguYihQLnJyKGMsYSxiKSl9LApYZDpmdW5jdGlvbihhLGIsYyxk
+KXt2YXIgcyxyLHEscCxvLG4sbSxsLGssaixpLGg9bnVsbCxnPWIubGVuZ3RoCmlmKGchPT0wKXtxPTAK
+d2hpbGUoITApe2lmKCEocTxnKSl7cz0iIgpyPTAKYnJlYWt9aWYoQy54Qi5XKGIscSk9PT02NCl7cz1D
+LnhCLk5qKGIsMCxxKQpyPXErMQpicmVha30rK3F9aWYocjxnJiZDLnhCLlcoYixyKT09PTkxKXtmb3Io
+cD1yLG89LTE7cDxnOysrcCl7bj1DLnhCLlcoYixwKQppZihuPT09MzcmJm88MCl7bT1DLnhCLlFpKGIs
+IjI1IixwKzEpP3ArMjpwCm89cApwPW19ZWxzZSBpZihuPT09OTMpYnJlYWt9aWYocD09PWcpdGhyb3cg
+SC5iKFAucnIoIkludmFsaWQgSVB2NiBob3N0IGVudHJ5LiIsYixyKSkKbD1vPDA/cDpvClAuZWcoYixy
+KzEsbCk7KytwCmlmKHAhPT1nJiZDLnhCLlcoYixwKSE9PTU4KXRocm93IEguYihQLnJyKCJJbnZhbGlk
+IGVuZCBvZiBhdXRob3JpdHkiLGIscCkpfWVsc2UgcD1yCndoaWxlKCEwKXtpZighKHA8Zykpe2s9aApi
+cmVha31pZihDLnhCLlcoYixwKT09PTU4KXtqPUMueEIueW4oYixwKzEpCms9ai5sZW5ndGghPT0wP1Au
+UUEoaixoKTpoCmJyZWFrfSsrcH1pPUMueEIuTmooYixyLHApfWVsc2V7az1oCmk9awpzPSIifXJldHVy
+biBQLktMKGksaCxILlFJKGMuc3BsaXQoIi8iKSx0LnMpLGssZCxhLHMpfSwKa0U6ZnVuY3Rpb24oYSxi
+KXt2YXIgcyxyLHEscCxvCmZvcihzPWEubGVuZ3RoLHI9MDtyPHM7KytyKXtxPWFbcl0KcD1KLlU2KHEp
+Cm89cC5nQShxKQppZigwPm8pSC52KFAuVEUoMCwwLHAuZ0EocSksbnVsbCxudWxsKSkKaWYoSC5TUShx
+LCIvIiwwKSl7cz1QLkw0KCJJbGxlZ2FsIHBhdGggY2hhcmFjdGVyICIrSC5FaihxKSkKdGhyb3cgSC5i
+KHMpfX19LApITjpmdW5jdGlvbihhLGIsYyl7dmFyIHMscixxLHAKZm9yKHM9SC5xQyhhLGMsbnVsbCxI
+LnQ2KGEpLmMpLHI9cy4kdGkscz1uZXcgSC5hNyhzLHMuZ0Eocyksci5DKCJhNzxhTC5FPiIpKSxyPXIu
+QygiYUwuRSIpO3MuRigpOyl7cT1yLmEocy5kKQpwPVAubnUoJ1siKi86PD4/XFxcXHxdJykKaWYoSC5T
+UShxLHAsMCkpe3M9UC5MNCgiSWxsZWdhbCBjaGFyYWN0ZXIgaW4gcGF0aDogIitxKQp0aHJvdyBILmIo
+cyl9fX0sCnJnOmZ1bmN0aW9uKGEsYil7dmFyIHMKaWYoISg2NTw9YSYmYTw9OTApKXM9OTc8PWEmJmE8
+PTEyMgplbHNlIHM9ITAKaWYocylyZXR1cm4Kcz1QLkw0KCJJbGxlZ2FsIGRyaXZlIGxldHRlciAiK1Au
+T28oYSkpCnRocm93IEguYihzKX0sCndCOmZ1bmN0aW9uKGEsYil7aWYoYSE9bnVsbCYmYT09PVAud0so
+YikpcmV0dXJuIG51bGwKcmV0dXJuIGF9LApPZTpmdW5jdGlvbihhLGIsYyxkKXt2YXIgcyxyLHEscCxv
+LG4KaWYoYT09bnVsbClyZXR1cm4gbnVsbAppZihiPT09YylyZXR1cm4iIgppZihDLnhCLk8oYSxiKT09
+PTkxKXtzPWMtMQppZihDLnhCLk8oYSxzKSE9PTkzKVAuUjMoYSxiLCJNaXNzaW5nIGVuZCBgXWAgdG8g
+bWF0Y2ggYFtgIGluIGhvc3QiKQpyPWIrMQpxPVAudG8oYSxyLHMpCmlmKHE8cyl7cD1xKzEKbz1QLk9B
+KGEsQy54Qi5RaShhLCIyNSIscCk/cSszOnAscywiJTI1Iil9ZWxzZSBvPSIiClAuZWcoYSxyLHEpCnJl
+dHVybiBDLnhCLk5qKGEsYixxKS50b0xvd2VyQ2FzZSgpK28rIl0ifWZvcihuPWI7bjxjOysrbilpZihD
+LnhCLk8oYSxuKT09PTU4KXtxPUMueEIuWFUoYSwiJSIsYikKcT1xPj1iJiZxPGM/cTpjCmlmKHE8Yyl7
+cD1xKzEKbz1QLk9BKGEsQy54Qi5RaShhLCIyNSIscCk/cSszOnAsYywiJTI1Iil9ZWxzZSBvPSIiClAu
+ZWcoYSxiLHEpCnJldHVybiJbIitDLnhCLk5qKGEsYixxKStvKyJdIn1yZXR1cm4gUC5PTChhLGIsYyl9
+LAp0bzpmdW5jdGlvbihhLGIsYyl7dmFyIHM9Qy54Qi5YVShhLCIlIixiKQpyZXR1cm4gcz49YiYmczxj
+P3M6Y30sCk9BOmZ1bmN0aW9uKGEsYixjLGQpe3ZhciBzLHIscSxwLG8sbixtLGwsayxqLGk9ZCE9PSIi
+P25ldyBQLk0oZCk6bnVsbApmb3Iocz1iLHI9cyxxPSEwO3M8Yzspe3A9Qy54Qi5PKGEscykKaWYocD09
+PTM3KXtvPVAucnYoYSxzLCEwKQpuPW89PW51bGwKaWYobiYmcSl7cys9Mwpjb250aW51ZX1pZihpPT1u
+dWxsKWk9bmV3IFAuTSgiIikKbT1pLmErPUMueEIuTmooYSxyLHMpCmlmKG4pbz1DLnhCLk5qKGEscyxz
+KzMpCmVsc2UgaWYobz09PSIlIilQLlIzKGEscywiWm9uZUlEIHNob3VsZCBub3QgY29udGFpbiAlIGFu
+eW1vcmUiKQppLmE9bStvCnMrPTMKcj1zCnE9ITB9ZWxzZXtpZihwPDEyNyl7bj1wPj4+NAppZihuPj04
+KXJldHVybiBILk9IKEMuRjMsbikKbj0oQy5GM1tuXSYxPDwocCYxNSkpIT09MH1lbHNlIG49ITEKaWYo
+bil7aWYocSYmNjU8PXAmJjkwPj1wKXtpZihpPT1udWxsKWk9bmV3IFAuTSgiIikKaWYocjxzKXtpLmEr
+PUMueEIuTmooYSxyLHMpCnI9c31xPSExfSsrc31lbHNle2lmKChwJjY0NTEyKT09PTU1Mjk2JiZzKzE8
+Yyl7bD1DLnhCLk8oYSxzKzEpCmlmKChsJjY0NTEyKT09PTU2MzIwKXtwPShwJjEwMjMpPDwxMHxsJjEw
+MjN8NjU1MzYKaz0yfWVsc2Ugaz0xfWVsc2Ugaz0xCmo9Qy54Qi5OaihhLHIscykKaWYoaT09bnVsbCl7
+aT1uZXcgUC5NKCIiKQpuPWl9ZWxzZSBuPWkKbi5hKz1qCm4uYSs9UC56WChwKQpzKz1rCnI9c319fWlm
+KGk9PW51bGwpcmV0dXJuIEMueEIuTmooYSxiLGMpCmlmKHI8YylpLmErPUMueEIuTmooYSxyLGMpCm49
+aS5hCnJldHVybiBuLmNoYXJDb2RlQXQoMCk9PTA/bjpufSwKT0w6ZnVuY3Rpb24oYSxiLGMpe3ZhciBz
+LHIscSxwLG8sbixtLGwsayxqLGkKZm9yKHM9YixyPXMscT1udWxsLHA9ITA7czxjOyl7bz1DLnhCLk8o
+YSxzKQppZihvPT09Mzcpe249UC5ydihhLHMsITApCm09bj09bnVsbAppZihtJiZwKXtzKz0zCmNvbnRp
+bnVlfWlmKHE9PW51bGwpcT1uZXcgUC5NKCIiKQpsPUMueEIuTmooYSxyLHMpCms9cS5hKz0hcD9sLnRv
+TG93ZXJDYXNlKCk6bAppZihtKXtuPUMueEIuTmooYSxzLHMrMykKaj0zfWVsc2UgaWYobj09PSIlIil7
+bj0iJTI1IgpqPTF9ZWxzZSBqPTMKcS5hPWsrbgpzKz1qCnI9cwpwPSEwfWVsc2V7aWYobzwxMjcpe209
+bz4+PjQKaWYobT49OClyZXR1cm4gSC5PSChDLmVhLG0pCm09KEMuZWFbbV0mMTw8KG8mMTUpKSE9PTB9
+ZWxzZSBtPSExCmlmKG0pe2lmKHAmJjY1PD1vJiY5MD49byl7aWYocT09bnVsbClxPW5ldyBQLk0oIiIp
+CmlmKHI8cyl7cS5hKz1DLnhCLk5qKGEscixzKQpyPXN9cD0hMX0rK3N9ZWxzZXtpZihvPD05Myl7bT1v
+Pj4+NAppZihtPj04KXJldHVybiBILk9IKEMuYWssbSkKbT0oQy5ha1ttXSYxPDwobyYxNSkpIT09MH1l
+bHNlIG09ITEKaWYobSlQLlIzKGEscywiSW52YWxpZCBjaGFyYWN0ZXIiKQplbHNle2lmKChvJjY0NTEy
+KT09PTU1Mjk2JiZzKzE8Yyl7aT1DLnhCLk8oYSxzKzEpCmlmKChpJjY0NTEyKT09PTU2MzIwKXtvPShv
+JjEwMjMpPDwxMHxpJjEwMjN8NjU1MzYKaj0yfWVsc2Ugaj0xfWVsc2Ugaj0xCmw9Qy54Qi5OaihhLHIs
+cykKaWYoIXApbD1sLnRvTG93ZXJDYXNlKCkKaWYocT09bnVsbCl7cT1uZXcgUC5NKCIiKQptPXF9ZWxz
+ZSBtPXEKbS5hKz1sCm0uYSs9UC56WChvKQpzKz1qCnI9c319fX1pZihxPT1udWxsKXJldHVybiBDLnhC
+Lk5qKGEsYixjKQppZihyPGMpe2w9Qy54Qi5OaihhLHIsYykKcS5hKz0hcD9sLnRvTG93ZXJDYXNlKCk6
+bH1tPXEuYQpyZXR1cm4gbS5jaGFyQ29kZUF0KDApPT0wP206bX0sClBpOmZ1bmN0aW9uKGEsYixjKXt2
+YXIgcyxyLHEscAppZihiPT09YylyZXR1cm4iIgppZighUC5FdChDLnhCLlcoYSxiKSkpUC5SMyhhLGIs
+IlNjaGVtZSBub3Qgc3RhcnRpbmcgd2l0aCBhbHBoYWJldGljIGNoYXJhY3RlciIpCmZvcihzPWIscj0h
+MTtzPGM7KytzKXtxPUMueEIuVyhhLHMpCmlmKHE8MTI4KXtwPXE+Pj40CmlmKHA+PTgpcmV0dXJuIEgu
+T0goQy5tSyxwKQpwPShDLm1LW3BdJjE8PChxJjE1KSkhPT0wfWVsc2UgcD0hMQppZighcClQLlIzKGEs
+cywiSWxsZWdhbCBzY2hlbWUgY2hhcmFjdGVyIikKaWYoNjU8PXEmJnE8PTkwKXI9ITB9YT1DLnhCLk5q
+KGEsYixjKQpyZXR1cm4gUC5ZYShyP2EudG9Mb3dlckNhc2UoKTphKX0sCllhOmZ1bmN0aW9uKGEpe2lm
+KGE9PT0iaHR0cCIpcmV0dXJuImh0dHAiCmlmKGE9PT0iZmlsZSIpcmV0dXJuImZpbGUiCmlmKGE9PT0i
+aHR0cHMiKXJldHVybiJodHRwcyIKaWYoYT09PSJwYWNrYWdlIilyZXR1cm4icGFja2FnZSIKcmV0dXJu
+IGF9LAp6UjpmdW5jdGlvbihhLGIsYyl7aWYoYT09bnVsbClyZXR1cm4iIgpyZXR1cm4gUC5QSShhLGIs
+YyxDLnRvLCExKX0sCmthOmZ1bmN0aW9uKGEsYixjLGQsZSxmKXt2YXIgcyxyLHE9ZT09PSJmaWxlIixw
+PXF8fGYKaWYoYT09bnVsbCl7aWYoZD09bnVsbClyZXR1cm4gcT8iLyI6IiIKcz1ILnQ2KGQpCnI9bmV3
+IEgubEooZCxzLkMoInFVKDEpIikuYShuZXcgUC5SWigpKSxzLkMoImxKPDEscVU+IikpLmsoMCwiLyIp
+fWVsc2UgaWYoZCE9bnVsbCl0aHJvdyBILmIoUC54WSgiQm90aCBwYXRoIGFuZCBwYXRoU2VnbWVudHMg
+c3BlY2lmaWVkIikpCmVsc2Ugcj1QLlBJKGEsYixjLEMuV2QsITApCmlmKHIubGVuZ3RoPT09MCl7aWYo
+cSlyZXR1cm4iLyJ9ZWxzZSBpZihwJiYhQy54Qi5uKHIsIi8iKSlyPSIvIityCnJldHVybiBQLkpyKHIs
+ZSxmKX0sCkpyOmZ1bmN0aW9uKGEsYixjKXt2YXIgcz1iLmxlbmd0aD09PTAKaWYocyYmIWMmJiFDLnhC
+Lm4oYSwiLyIpKXJldHVybiBQLndGKGEsIXN8fGMpCnJldHVybiBQLnhlKGEpfSwKbGU6ZnVuY3Rpb24o
+YSxiLGMsZCl7dmFyIHMscj17fQppZihhIT1udWxsKXtpZihkIT1udWxsKXRocm93IEguYihQLnhZKCJC
+b3RoIHF1ZXJ5IGFuZCBxdWVyeVBhcmFtZXRlcnMgc3BlY2lmaWVkIikpCnJldHVybiBQLlBJKGEsYixj
+LEMuVkMsITApfWlmKGQ9PW51bGwpcmV0dXJuIG51bGwKcz1uZXcgUC5NKCIiKQpyLmE9IiIKZC5LKDAs
+bmV3IFAueTUobmV3IFAuTUUocixzKSkpCnI9cy5hCnJldHVybiByLmNoYXJDb2RlQXQoMCk9PTA/cjpy
+fSwKdEc6ZnVuY3Rpb24oYSxiLGMpe2lmKGE9PW51bGwpcmV0dXJuIG51bGwKcmV0dXJuIFAuUEkoYSxi
+LGMsQy5WQywhMCl9LApydjpmdW5jdGlvbihhLGIsYyl7dmFyIHMscixxLHAsbyxuPWIrMgppZihuPj1h
+Lmxlbmd0aClyZXR1cm4iJSIKcz1DLnhCLk8oYSxiKzEpCnI9Qy54Qi5PKGEsbikKcT1ILm9vKHMpCnA9
+SC5vbyhyKQppZihxPDB8fHA8MClyZXR1cm4iJSIKbz1xKjE2K3AKaWYobzwxMjcpe249Qy5qbi53Ryhv
+LDQpCmlmKG4+PTgpcmV0dXJuIEguT0goQy5GMyxuKQpuPShDLkYzW25dJjE8PChvJjE1KSkhPT0wfWVs
+c2Ugbj0hMQppZihuKXJldHVybiBILkx3KGMmJjY1PD1vJiY5MD49bz8ob3wzMik+Pj4wOm8pCmlmKHM+
+PTk3fHxyPj05NylyZXR1cm4gQy54Qi5OaihhLGIsYiszKS50b1VwcGVyQ2FzZSgpCnJldHVybiBudWxs
+fSwKelg6ZnVuY3Rpb24oYSl7dmFyIHMscixxLHAsbyxuLG0sbCxrPSIwMTIzNDU2Nzg5QUJDREVGIgpp
+ZihhPDEyOCl7cz1uZXcgVWludDhBcnJheSgzKQpzWzBdPTM3CnNbMV09Qy54Qi5XKGssYT4+PjQpCnNb
+Ml09Qy54Qi5XKGssYSYxNSl9ZWxzZXtpZihhPjIwNDcpaWYoYT42NTUzNSl7cj0yNDAKcT00fWVsc2V7
+cj0yMjQKcT0zfWVsc2V7cj0xOTIKcT0yfXA9MypxCnM9bmV3IFVpbnQ4QXJyYXkocCkKZm9yKG89MDst
+LXEscT49MDtyPTEyOCl7bj1DLmpuLmJmKGEsNipxKSY2M3xyCmlmKG8+PXApcmV0dXJuIEguT0gocyxv
+KQpzW29dPTM3Cm09bysxCmw9Qy54Qi5XKGssbj4+PjQpCmlmKG0+PXApcmV0dXJuIEguT0gocyxtKQpz
+W21dPWwKbD1vKzIKbT1DLnhCLlcoayxuJjE1KQppZihsPj1wKXJldHVybiBILk9IKHMsbCkKc1tsXT1t
+Cm8rPTN9fXJldHVybiBQLkhNKHMsMCxudWxsKX0sClBJOmZ1bmN0aW9uKGEsYixjLGQsZSl7dmFyIHM9
+UC5VbChhLGIsYyxkLGUpCnJldHVybiBzPT1udWxsP0MueEIuTmooYSxiLGMpOnN9LApVbDpmdW5jdGlv
+bihhLGIsYyxkLGUpe3ZhciBzLHIscSxwLG8sbixtLGwsayxqPW51bGwKZm9yKHM9IWUscj1iLHE9cixw
+PWo7cjxjOyl7bz1DLnhCLk8oYSxyKQppZihvPDEyNyl7bj1vPj4+NAppZihuPj04KXJldHVybiBILk9I
+KGQsbikKbj0oZFtuXSYxPDwobyYxNSkpIT09MH1lbHNlIG49ITEKaWYobikrK3IKZWxzZXtpZihvPT09
+Mzcpe209UC5ydihhLHIsITEpCmlmKG09PW51bGwpe3IrPTMKY29udGludWV9aWYoIiUiPT09bSl7bT0i
+JTI1IgpsPTF9ZWxzZSBsPTN9ZWxzZXtpZihzKWlmKG88PTkzKXtuPW8+Pj40CmlmKG4+PTgpcmV0dXJu
+IEguT0goQy5hayxuKQpuPShDLmFrW25dJjE8PChvJjE1KSkhPT0wfWVsc2Ugbj0hMQplbHNlIG49ITEK
+aWYobil7UC5SMyhhLHIsIkludmFsaWQgY2hhcmFjdGVyIikKbD1qCm09bH1lbHNle2lmKChvJjY0NTEy
+KT09PTU1Mjk2KXtuPXIrMQppZihuPGMpe2s9Qy54Qi5PKGEsbikKaWYoKGsmNjQ1MTIpPT09NTYzMjAp
+e289KG8mMTAyMyk8PDEwfGsmMTAyM3w2NTUzNgpsPTJ9ZWxzZSBsPTF9ZWxzZSBsPTF9ZWxzZSBsPTEK
+bT1QLnpYKG8pfX1pZihwPT1udWxsKXtwPW5ldyBQLk0oIiIpCm49cH1lbHNlIG49cApuLmErPUMueEIu
+TmooYSxxLHIpCm4uYSs9SC5FaihtKQppZih0eXBlb2YgbCE9PSJudW1iZXIiKXJldHVybiBILnBZKGwp
+CnIrPWwKcT1yfX1pZihwPT1udWxsKXJldHVybiBqCmlmKHE8YylwLmErPUMueEIuTmooYSxxLGMpCnM9
+cC5hCnJldHVybiBzLmNoYXJDb2RlQXQoMCk9PTA/czpzfSwKeUI6ZnVuY3Rpb24oYSl7aWYoQy54Qi5u
+KGEsIi4iKSlyZXR1cm4hMApyZXR1cm4gQy54Qi5PWShhLCIvLiIpIT09LTF9LAp4ZTpmdW5jdGlvbihh
+KXt2YXIgcyxyLHEscCxvLG4sbQppZighUC55QihhKSlyZXR1cm4gYQpzPUguUUkoW10sdC5zKQpmb3Io
+cj1hLnNwbGl0KCIvIikscT1yLmxlbmd0aCxwPSExLG89MDtvPHE7KytvKXtuPXJbb10KaWYoSi5STShu
+LCIuLiIpKXttPXMubGVuZ3RoCmlmKG0hPT0wKXtpZigwPj1tKXJldHVybiBILk9IKHMsLTEpCnMucG9w
+KCkKaWYocy5sZW5ndGg9PT0wKUMuTm0uaShzLCIiKX1wPSEwfWVsc2UgaWYoIi4iPT09bilwPSEwCmVs
+c2V7Qy5ObS5pKHMsbikKcD0hMX19aWYocClDLk5tLmkocywiIikKcmV0dXJuIEMuTm0uayhzLCIvIil9
+LAp3RjpmdW5jdGlvbihhLGIpe3ZhciBzLHIscSxwLG8sbgppZighUC55QihhKSlyZXR1cm4hYj9QLkMx
+KGEpOmEKcz1ILlFJKFtdLHQucykKZm9yKHI9YS5zcGxpdCgiLyIpLHE9ci5sZW5ndGgscD0hMSxvPTA7
+bzxxOysrbyl7bj1yW29dCmlmKCIuLiI9PT1uKWlmKHMubGVuZ3RoIT09MCYmQy5ObS5ncloocykhPT0i
+Li4iKXtpZigwPj1zLmxlbmd0aClyZXR1cm4gSC5PSChzLC0xKQpzLnBvcCgpCnA9ITB9ZWxzZXtDLk5t
+LmkocywiLi4iKQpwPSExfWVsc2UgaWYoIi4iPT09bilwPSEwCmVsc2V7Qy5ObS5pKHMsbikKcD0hMX19
+cj1zLmxlbmd0aAppZihyIT09MClpZihyPT09MSl7aWYoMD49cilyZXR1cm4gSC5PSChzLDApCnI9c1sw
+XS5sZW5ndGg9PT0wfWVsc2Ugcj0hMQplbHNlIHI9ITAKaWYocilyZXR1cm4iLi8iCmlmKHB8fEMuTm0u
+Z3JaKHMpPT09Ii4uIilDLk5tLmkocywiIikKaWYoIWIpe2lmKDA+PXMubGVuZ3RoKXJldHVybiBILk9I
+KHMsMCkKQy5ObS5ZNShzLDAsUC5DMShzWzBdKSl9cmV0dXJuIEMuTm0uayhzLCIvIil9LApDMTpmdW5j
+dGlvbihhKXt2YXIgcyxyLHEscD1hLmxlbmd0aAppZihwPj0yJiZQLkV0KEMueEIuVyhhLDApKSlmb3Io
+cz0xO3M8cDsrK3Mpe3I9Qy54Qi5XKGEscykKaWYocj09PTU4KXJldHVybiBDLnhCLk5qKGEsMCxzKSsi
+JTNBIitDLnhCLnluKGEscysxKQppZihyPD0xMjcpe3E9cj4+PjQKaWYocT49OClyZXR1cm4gSC5PSChD
+Lm1LLHEpCnE9KEMubUtbcV0mMTw8KHImMTUpKT09PTB9ZWxzZSBxPSEwCmlmKHEpYnJlYWt9cmV0dXJu
+IGF9LAp1ajpmdW5jdGlvbihhLGIpe2lmKGEuaEIoInBhY2thZ2UiKSYmYS5jPT1udWxsKXJldHVybiBQ
+LmZGKGIsMCxiLmxlbmd0aCkKcmV0dXJuLTF9LAptbjpmdW5jdGlvbihhKXt2YXIgcyxyLHEscD1hLmdG
+aigpLG89cC5sZW5ndGgKaWYobz4wJiZKLkhtKHBbMF0pPT09MiYmSi5hNihwWzBdLDEpPT09NTgpe2lm
+KDA+PW8pcmV0dXJuIEguT0gocCwwKQpQLnJnKEouYTYocFswXSwwKSwhMSkKUC5ITihwLCExLDEpCnM9
+ITB9ZWxzZXtQLkhOKHAsITEsMCkKcz0hMX1yPWEuZ3RUKCkmJiFzPyIiKyJcXCI6IiIKaWYoYS5nY2oo
+KSl7cT1hLmdKZihhKQppZihxLmxlbmd0aCE9PTApcj1yKyJcXCIrcSsiXFwifXI9UC5sKHIscCwiXFwi
+KQpvPXMmJm89PT0xP3IrIlxcIjpyCnJldHVybiBvLmNoYXJDb2RlQXQoMCk9PTA/bzpvfSwKSWg6ZnVu
+Y3Rpb24oYSxiKXt2YXIgcyxyLHEKZm9yKHM9MCxyPTA7cjwyOysrcil7cT1DLnhCLlcoYSxiK3IpCmlm
+KDQ4PD1xJiZxPD01NylzPXMqMTYrcS00OAplbHNle3F8PTMyCmlmKDk3PD1xJiZxPD0xMDIpcz1zKjE2
+K3EtODcKZWxzZSB0aHJvdyBILmIoUC54WSgiSW52YWxpZCBVUkwgZW5jb2RpbmciKSl9fXJldHVybiBz
+fSwKa3U6ZnVuY3Rpb24oYSxiLGMsZCxlKXt2YXIgcyxyLHEscCxvPWIKd2hpbGUoITApe2lmKCEobzxj
+KSl7cz0hMApicmVha31yPUMueEIuVyhhLG8pCmlmKHI8PTEyNylpZihyIT09MzcpcT1lJiZyPT09NDMK
+ZWxzZSBxPSEwCmVsc2UgcT0hMAppZihxKXtzPSExCmJyZWFrfSsrb31pZihzKXtpZihDLnhNIT09ZClx
+PSExCmVsc2UgcT0hMAppZihxKXJldHVybiBDLnhCLk5qKGEsYixjKQplbHNlIHA9bmV3IEgucWooQy54
+Qi5OaihhLGIsYykpfWVsc2V7cD1ILlFJKFtdLHQudCkKZm9yKHE9YS5sZW5ndGgsbz1iO288YzsrK28p
+e3I9Qy54Qi5XKGEsbykKaWYocj4xMjcpdGhyb3cgSC5iKFAueFkoIklsbGVnYWwgcGVyY2VudCBlbmNv
+ZGluZyBpbiBVUkkiKSkKaWYocj09PTM3KXtpZihvKzM+cSl0aHJvdyBILmIoUC54WSgiVHJ1bmNhdGVk
+IFVSSSIpKQpDLk5tLmkocCxQLkloKGEsbysxKSkKbys9Mn1lbHNlIGlmKGUmJnI9PT00MylDLk5tLmko
+cCwzMikKZWxzZSBDLk5tLmkocCxyKX19dC5MLmEocCkKcmV0dXJuIEMub0UuV0oocCl9LApFdDpmdW5j
+dGlvbihhKXt2YXIgcz1hfDMyCnJldHVybiA5Nzw9cyYmczw9MTIyfSwKS0Q6ZnVuY3Rpb24oYSxiLGMp
+e3ZhciBzLHIscSxwLG8sbixtLGwsaz0iSW52YWxpZCBNSU1FIHR5cGUiLGo9SC5RSShbYi0xXSx0LnQp
+CmZvcihzPWEubGVuZ3RoLHI9YixxPS0xLHA9bnVsbDtyPHM7KytyKXtwPUMueEIuVyhhLHIpCmlmKHA9
+PT00NHx8cD09PTU5KWJyZWFrCmlmKHA9PT00Nyl7aWYocTwwKXtxPXIKY29udGludWV9dGhyb3cgSC5i
+KFAucnIoayxhLHIpKX19aWYocTwwJiZyPmIpdGhyb3cgSC5iKFAucnIoayxhLHIpKQpmb3IoO3AhPT00
+NDspe0MuTm0uaShqLHIpOysrcgpmb3Iobz0tMTtyPHM7KytyKXtwPUMueEIuVyhhLHIpCmlmKHA9PT02
+MSl7aWYobzwwKW89cn1lbHNlIGlmKHA9PT01OXx8cD09PTQ0KWJyZWFrfWlmKG8+PTApQy5ObS5pKGos
+bykKZWxzZXtuPUMuTm0uZ3JaKGopCmlmKHAhPT00NHx8ciE9PW4rN3x8IUMueEIuUWkoYSwiYmFzZTY0
+IixuKzEpKXRocm93IEguYihQLnJyKCJFeHBlY3RpbmcgJz0nIixhLHIpKQpicmVha319Qy5ObS5pKGos
+cikKbT1yKzEKaWYoKGoubGVuZ3RoJjEpPT09MSlhPUMuaDkueXIoYSxtLHMpCmVsc2V7bD1QLlVsKGEs
+bSxzLEMuVkMsITApCmlmKGwhPW51bGwpYT1DLnhCLmk3KGEsbSxzLGwpfXJldHVybiBuZXcgUC5QRShh
+LGosYyl9LApLTjpmdW5jdGlvbigpe3ZhciBzLHIscSxwLG8sbixtPSIwMTIzNDU2Nzg5QUJDREVGR0hJ
+SktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ei0uX34hJCYnKCkqKyw7PSIs
+bD0iLiIsaz0iOiIsaj0iLyIsaT0iPyIsaD0iIyIsZz1ILlFJKG5ldyBBcnJheSgyMiksdC5nTikKZm9y
+KHM9MDtzPDIyOysrcylnW3NdPW5ldyBVaW50OEFycmF5KDk2KQpyPW5ldyBQLnlJKGcpCnE9bmV3IFAu
+YzYoKQpwPW5ldyBQLnFkKCkKbz10LmdjCm49by5hKHIuJDIoMCwyMjUpKQpxLiQzKG4sbSwxKQpxLiQz
+KG4sbCwxNCkKcS4kMyhuLGssMzQpCnEuJDMobixqLDMpCnEuJDMobixpLDE3MikKcS4kMyhuLGgsMjA1
+KQpuPW8uYShyLiQyKDE0LDIyNSkpCnEuJDMobixtLDEpCnEuJDMobixsLDE1KQpxLiQzKG4saywzNCkK
+cS4kMyhuLGosMjM0KQpxLiQzKG4saSwxNzIpCnEuJDMobixoLDIwNSkKbj1vLmEoci4kMigxNSwyMjUp
+KQpxLiQzKG4sbSwxKQpxLiQzKG4sIiUiLDIyNSkKcS4kMyhuLGssMzQpCnEuJDMobixqLDkpCnEuJDMo
+bixpLDE3MikKcS4kMyhuLGgsMjA1KQpuPW8uYShyLiQyKDEsMjI1KSkKcS4kMyhuLG0sMSkKcS4kMyhu
+LGssMzQpCnEuJDMobixqLDEwKQpxLiQzKG4saSwxNzIpCnEuJDMobixoLDIwNSkKbj1vLmEoci4kMigy
+LDIzNSkpCnEuJDMobixtLDEzOSkKcS4kMyhuLGosMTMxKQpxLiQzKG4sbCwxNDYpCnEuJDMobixpLDE3
+MikKcS4kMyhuLGgsMjA1KQpuPW8uYShyLiQyKDMsMjM1KSkKcS4kMyhuLG0sMTEpCnEuJDMobixqLDY4
+KQpxLiQzKG4sbCwxOCkKcS4kMyhuLGksMTcyKQpxLiQzKG4saCwyMDUpCm49by5hKHIuJDIoNCwyMjkp
+KQpxLiQzKG4sbSw1KQpwLiQzKG4sIkFaIiwyMjkpCnEuJDMobixrLDEwMikKcS4kMyhuLCJAIiw2OCkK
+cS4kMyhuLCJbIiwyMzIpCnEuJDMobixqLDEzOCkKcS4kMyhuLGksMTcyKQpxLiQzKG4saCwyMDUpCm49
+by5hKHIuJDIoNSwyMjkpKQpxLiQzKG4sbSw1KQpwLiQzKG4sIkFaIiwyMjkpCnEuJDMobixrLDEwMikK
+cS4kMyhuLCJAIiw2OCkKcS4kMyhuLGosMTM4KQpxLiQzKG4saSwxNzIpCnEuJDMobixoLDIwNSkKbj1v
+LmEoci4kMig2LDIzMSkpCnAuJDMobiwiMTkiLDcpCnEuJDMobiwiQCIsNjgpCnEuJDMobixqLDEzOCkK
+cS4kMyhuLGksMTcyKQpxLiQzKG4saCwyMDUpCm49by5hKHIuJDIoNywyMzEpKQpwLiQzKG4sIjA5Iiw3
+KQpxLiQzKG4sIkAiLDY4KQpxLiQzKG4saiwxMzgpCnEuJDMobixpLDE3MikKcS4kMyhuLGgsMjA1KQpx
+LiQzKG8uYShyLiQyKDgsOCkpLCJdIiw1KQpuPW8uYShyLiQyKDksMjM1KSkKcS4kMyhuLG0sMTEpCnEu
+JDMobixsLDE2KQpxLiQzKG4saiwyMzQpCnEuJDMobixpLDE3MikKcS4kMyhuLGgsMjA1KQpuPW8uYShy
+LiQyKDE2LDIzNSkpCnEuJDMobixtLDExKQpxLiQzKG4sbCwxNykKcS4kMyhuLGosMjM0KQpxLiQzKG4s
+aSwxNzIpCnEuJDMobixoLDIwNSkKbj1vLmEoci4kMigxNywyMzUpKQpxLiQzKG4sbSwxMSkKcS4kMyhu
+LGosOSkKcS4kMyhuLGksMTcyKQpxLiQzKG4saCwyMDUpCm49by5hKHIuJDIoMTAsMjM1KSkKcS4kMyhu
+LG0sMTEpCnEuJDMobixsLDE4KQpxLiQzKG4saiwyMzQpCnEuJDMobixpLDE3MikKcS4kMyhuLGgsMjA1
+KQpuPW8uYShyLiQyKDE4LDIzNSkpCnEuJDMobixtLDExKQpxLiQzKG4sbCwxOSkKcS4kMyhuLGosMjM0
+KQpxLiQzKG4saSwxNzIpCnEuJDMobixoLDIwNSkKbj1vLmEoci4kMigxOSwyMzUpKQpxLiQzKG4sbSwx
+MSkKcS4kMyhuLGosMjM0KQpxLiQzKG4saSwxNzIpCnEuJDMobixoLDIwNSkKbj1vLmEoci4kMigxMSwy
+MzUpKQpxLiQzKG4sbSwxMSkKcS4kMyhuLGosMTApCnEuJDMobixpLDE3MikKcS4kMyhuLGgsMjA1KQpu
+PW8uYShyLiQyKDEyLDIzNikpCnEuJDMobixtLDEyKQpxLiQzKG4saSwxMikKcS4kMyhuLGgsMjA1KQpu
+PW8uYShyLiQyKDEzLDIzNykpCnEuJDMobixtLDEzKQpxLiQzKG4saSwxMykKcC4kMyhvLmEoci4kMigy
+MCwyNDUpKSwiYXoiLDIxKQpyPW8uYShyLiQyKDIxLDI0NSkpCnAuJDMociwiYXoiLDIxKQpwLiQzKHIs
+IjA5IiwyMSkKcS4kMyhyLCIrLS4iLDIxKQpyZXR1cm4gZ30sClVCOmZ1bmN0aW9uKGEsYixjLGQsZSl7
+dmFyIHMscixxLHAsbz0kLnZaKCkKZm9yKHM9YjtzPGM7KytzKXtpZihkPDB8fGQ+PW8ubGVuZ3RoKXJl
+dHVybiBILk9IKG8sZCkKcj1vW2RdCnE9Qy54Qi5XKGEscyleOTYKcD1yW3E+OTU/MzE6cV0KZD1wJjMx
+CkMuTm0uWTUoZSxwPj4+NSxzKX1yZXR1cm4gZH0sClJ4OmZ1bmN0aW9uKGEpe2lmKGEuYj09PTcmJkMu
+eEIubihhLmEsInBhY2thZ2UiKSYmYS5jPD0wKXJldHVybiBQLmZGKGEuYSxhLmUsYS5mKQpyZXR1cm4t
+MX0sCmZGOmZ1bmN0aW9uKGEsYixjKXt2YXIgcyxyLHEKZm9yKHM9YixyPTA7czxjOysrcyl7cT1DLnhC
+Lk8oYSxzKQppZihxPT09NDcpcmV0dXJuIHIhPT0wP3M6LTEKaWYocT09PTM3fHxxPT09NTgpcmV0dXJu
+LTEKcnw9cV40Nn1yZXR1cm4tMX0sCldGOmZ1bmN0aW9uIFdGKGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9
+LAppUDpmdW5jdGlvbiBpUChhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKWFM6ZnVuY3Rpb24gWFMoKXt9
+LApDNjpmdW5jdGlvbiBDNihhKXt0aGlzLmE9YX0sCkV6OmZ1bmN0aW9uIEV6KCl7fSwKRjpmdW5jdGlv
+biBGKCl7fSwKQVQ6ZnVuY3Rpb24gQVQoYSxiLGMsZCl7dmFyIF89dGhpcwpfLmE9YQpfLmI9YgpfLmM9
+YwpfLmQ9ZH0sCmJKOmZ1bmN0aW9uIGJKKGEsYixjLGQsZSxmKXt2YXIgXz10aGlzCl8uZT1hCl8uZj1i
+Cl8uYT1jCl8uYj1kCl8uYz1lCl8uZD1mfSwKZVk6ZnVuY3Rpb24gZVkoYSxiLGMsZCxlKXt2YXIgXz10
+aGlzCl8uZj1hCl8uYT1iCl8uYj1jCl8uYz1kCl8uZD1lfSwKbXA6ZnVuY3Rpb24gbXAoYSxiLGMsZCl7
+dmFyIF89dGhpcwpfLmE9YQpfLmI9YgpfLmM9YwpfLmQ9ZH0sCnViOmZ1bmN0aW9uIHViKGEpe3RoaXMu
+YT1hfSwKZHM6ZnVuY3Rpb24gZHMoYSl7dGhpcy5hPWF9LApsajpmdW5jdGlvbiBsaihhKXt0aGlzLmE9
+YX0sClVWOmZ1bmN0aW9uIFVWKGEpe3RoaXMuYT1hfSwKazU6ZnVuY3Rpb24gazUoKXt9LApLWTpmdW5j
+dGlvbiBLWSgpe30sCnA6ZnVuY3Rpb24gcChhKXt0aGlzLmE9YX0sCkNEOmZ1bmN0aW9uIENEKGEpe3Ro
+aXMuYT1hfSwKYUU6ZnVuY3Rpb24gYUUoYSxiLGMpe3RoaXMuYT1hCnRoaXMuYj1iCnRoaXMuYz1jfSwK
+Y1g6ZnVuY3Rpb24gY1goKXt9LApBbjpmdW5jdGlvbiBBbigpe30sCk4zOmZ1bmN0aW9uIE4zKGEsYixj
+KXt0aGlzLmE9YQp0aGlzLmI9Ygp0aGlzLiR0aT1jfSwKYzg6ZnVuY3Rpb24gYzgoKXt9LApNaDpmdW5j
+dGlvbiBNaCgpe30sClpkOmZ1bmN0aW9uIFpkKCl7fSwKTTpmdW5jdGlvbiBNKGEpe3RoaXMuYT1hfSwK
+bjE6ZnVuY3Rpb24gbjEoYSl7dGhpcy5hPWF9LApjUzpmdW5jdGlvbiBjUyhhKXt0aGlzLmE9YX0sClZD
+OmZ1bmN0aW9uIFZDKGEpe3RoaXMuYT1hfSwKSlQ6ZnVuY3Rpb24gSlQoYSxiKXt0aGlzLmE9YQp0aGlz
+LmI9Yn0sCkRuOmZ1bmN0aW9uIERuKGEsYixjLGQsZSxmLGcpe3ZhciBfPXRoaXMKXy5hPWEKXy5iPWIK
+Xy5jPWMKXy5kPWQKXy5lPWUKXy5mPWYKXy5yPWcKXy5RPV8uej1fLnk9Xy54PW51bGx9LApSWjpmdW5j
+dGlvbiBSWigpe30sCk1FOmZ1bmN0aW9uIE1FKGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9LAp5NTpmdW5j
+dGlvbiB5NShhKXt0aGlzLmE9YX0sClBFOmZ1bmN0aW9uIFBFKGEsYixjKXt0aGlzLmE9YQp0aGlzLmI9
+Ygp0aGlzLmM9Y30sCnlJOmZ1bmN0aW9uIHlJKGEpe3RoaXMuYT1hfSwKYzY6ZnVuY3Rpb24gYzYoKXt9
+LApxZDpmdW5jdGlvbiBxZCgpe30sClVmOmZ1bmN0aW9uIFVmKGEsYixjLGQsZSxmLGcsaCl7dmFyIF89
+dGhpcwpfLmE9YQpfLmI9YgpfLmM9YwpfLmQ9ZApfLmU9ZQpfLmY9ZgpfLnI9ZwpfLng9aApfLnk9bnVs
+bH0sCnFlOmZ1bmN0aW9uIHFlKGEsYixjLGQsZSxmLGcpe3ZhciBfPXRoaXMKXy5hPWEKXy5iPWIKXy5j
+PWMKXy5kPWQKXy5lPWUKXy5mPWYKXy5yPWcKXy5RPV8uej1fLnk9Xy54PW51bGx9LAppSjpmdW5jdGlv
+biBpSigpe30sCkUyOmZ1bmN0aW9uIEUyKGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9LApqZzpmdW5jdGlv
+biBqZyhhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKQmY6ZnVuY3Rpb24gQmYoYSxiKXt0aGlzLmE9YQp0
+aGlzLmI9Yn0sCkFzOmZ1bmN0aW9uIEFzKCl7fSwKR0U6ZnVuY3Rpb24gR0UoYSl7dGhpcy5hPWF9LApO
+NzpmdW5jdGlvbiBONyhhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKdVE6ZnVuY3Rpb24gdVEoKXt9LApo
+RjpmdW5jdGlvbiBoRigpe30sClI0OmZ1bmN0aW9uKGEsYixjLGQpe3ZhciBzLHIscQpILnA4KGIpCnQu
+ai5hKGQpCmlmKGIpe3M9W2NdCkMuTm0uRlYocyxkKQpkPXN9cj10LnoKcT1QLkNIKEouTTEoZCxQLncw
+KCksciksITAscikKdC5ZLmEoYSkKcmV0dXJuIFAud1koSC5FayhhLHEsbnVsbCkpfSwKRG06ZnVuY3Rp
+b24oYSxiLGMpe3ZhciBzCnRyeXtpZihPYmplY3QuaXNFeHRlbnNpYmxlKGEpJiYhT2JqZWN0LnByb3Rv
+dHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKGEsYikpe09iamVjdC5kZWZpbmVQcm9wZXJ0eShhLGIse3Zh
+bHVlOmN9KQpyZXR1cm4hMH19Y2F0Y2gocyl7SC5SdShzKX1yZXR1cm4hMX0sCk9tOmZ1bmN0aW9uKGEs
+Yil7aWYoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKGEsYikpcmV0dXJuIGFbYl0K
+cmV0dXJuIG51bGx9LAp3WTpmdW5jdGlvbihhKXtpZihhPT1udWxsfHx0eXBlb2YgYT09InN0cmluZyJ8
+fHR5cGVvZiBhPT0ibnVtYmVyInx8SC5yUShhKSlyZXR1cm4gYQppZihhIGluc3RhbmNlb2YgUC5FNCly
+ZXR1cm4gYS5hCmlmKEguUjkoYSkpcmV0dXJuIGEKaWYodC5hay5iKGEpKXJldHVybiBhCmlmKGEgaW5z
+dGFuY2VvZiBQLmlQKXJldHVybiBILm8yKGEpCmlmKHQuWS5iKGEpKXJldHVybiBQLmhFKGEsIiRkYXJ0
+X2pzRnVuY3Rpb24iLG5ldyBQLlBDKCkpCnJldHVybiBQLmhFKGEsIl8kZGFydF9qc09iamVjdCIsbmV3
+IFAubXQoJC5rSSgpKSl9LApoRTpmdW5jdGlvbihhLGIsYyl7dmFyIHM9UC5PbShhLGIpCmlmKHM9PW51
+bGwpe3M9Yy4kMShhKQpQLkRtKGEsYixzKX1yZXR1cm4gc30sCmRVOmZ1bmN0aW9uKGEpe3ZhciBzLHIK
+aWYoYT09bnVsbHx8dHlwZW9mIGE9PSJzdHJpbmcifHx0eXBlb2YgYT09Im51bWJlciJ8fHR5cGVvZiBh
+PT0iYm9vbGVhbiIpcmV0dXJuIGEKZWxzZSBpZihhIGluc3RhbmNlb2YgT2JqZWN0JiZILlI5KGEpKXJl
+dHVybiBhCmVsc2UgaWYoYSBpbnN0YW5jZW9mIE9iamVjdCYmdC5hay5iKGEpKXJldHVybiBhCmVsc2Ug
+aWYoYSBpbnN0YW5jZW9mIERhdGUpe3M9SC5JWihhLmdldFRpbWUoKSkKaWYoTWF0aC5hYnMocyk8PTg2
+NGUxMylyPSExCmVsc2Ugcj0hMAppZihyKUgudihQLnhZKCJEYXRlVGltZSBpcyBvdXRzaWRlIHZhbGlk
+IHJhbmdlOiAiK3MpKQpILmNiKCExLCJpc1V0YyIsdC55KQpyZXR1cm4gbmV3IFAuaVAocywhMSl9ZWxz
+ZSBpZihhLmNvbnN0cnVjdG9yPT09JC5rSSgpKXJldHVybiBhLm8KZWxzZSByZXR1cm4gUC5ORChhKX0s
+Ck5EOmZ1bmN0aW9uKGEpe2lmKHR5cGVvZiBhPT0iZnVuY3Rpb24iKXJldHVybiBQLmlRKGEsJC56KCks
+bmV3IFAuUVMoKSkKaWYoYSBpbnN0YW5jZW9mIEFycmF5KXJldHVybiBQLmlRKGEsJC5SOCgpLG5ldyBQ
+Lm5wKCkpCnJldHVybiBQLmlRKGEsJC5SOCgpLG5ldyBQLlV0KCkpfSwKaVE6ZnVuY3Rpb24oYSxiLGMp
+e3ZhciBzPVAuT20oYSxiKQppZihzPT1udWxsfHwhKGEgaW5zdGFuY2VvZiBPYmplY3QpKXtzPWMuJDEo
+YSkKUC5EbShhLGIscyl9cmV0dXJuIHN9LApQQzpmdW5jdGlvbiBQQygpe30sCm10OmZ1bmN0aW9uIG10
+KGEpe3RoaXMuYT1hfSwKUVM6ZnVuY3Rpb24gUVMoKXt9LApucDpmdW5jdGlvbiBucCgpe30sClV0OmZ1
+bmN0aW9uIFV0KCl7fSwKRTQ6ZnVuY3Rpb24gRTQoYSl7dGhpcy5hPWF9LApyNzpmdW5jdGlvbiByNyhh
+KXt0aGlzLmE9YX0sClR6OmZ1bmN0aW9uIFR6KGEsYil7dGhpcy5hPWEKdGhpcy4kdGk9Yn0sCnZnOmZ1
+bmN0aW9uIHZnKCl7fSwKbmQ6ZnVuY3Rpb24gbmQoKXt9LApLZTpmdW5jdGlvbiBLZShhKXt0aGlzLmE9
+YX0sCmhpOmZ1bmN0aW9uIGhpKCl7fX0sUj17Cm56OmZ1bmN0aW9uKGEpe3ZhciBzPUguVWMoYS5xKDAs
+Im5vZGVJZCIpKQpyZXR1cm4gbmV3IFIuTEwoQy5ObS5IdChDLnJrLG5ldyBSLk1EKGEpKSxzKX0sCk9Y
+OmZ1bmN0aW9uKGEpe3N3aXRjaChhKXtjYXNlIEMuQWQ6cmV0dXJuIkFkZCAvKj8qLyBoaW50IgpjYXNl
+IEMubmU6cmV0dXJuIkFkZCAvKiEqLyBoaW50IgpjYXNlIEMud1Y6cmV0dXJuIlJlbW92ZSAvKj8qLyBo
+aW50IgpjYXNlIEMuZlI6cmV0dXJuIlJlbW92ZSAvKiEqLyBoaW50IgpjYXNlIEMubXk6cmV0dXJuIkNo
+YW5nZSB0byAvKj8qLyBoaW50IgpjYXNlIEMucng6cmV0dXJuIkNoYW5nZSB0byAvKiEqLyBoaW50In19
+LApMTDpmdW5jdGlvbiBMTChhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKTUQ6ZnVuY3Rpb24gTUQoYSl7
+dGhpcy5hPWF9LApINzpmdW5jdGlvbiBINyhhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifX0sVD17bVE6ZnVu
+Y3Rpb24gbVEoKXt9fSxVPXsKamY6ZnVuY3Rpb24oYSl7dmFyIHMscixxLHAKaWYoYT09bnVsbClzPW51
+bGwKZWxzZXtzPUguUUkoW10sdC5mQSkKZm9yKHI9Si5JVCh0LlIuYShhKSk7ci5GKCk7KXtxPXIuZ2wo
+KQpwPUouVTYocSkKcy5wdXNoKG5ldyBVLlNlKEguayhwLnEocSwiZGVzY3JpcHRpb24iKSksSC5rKHAu
+cShxLCJocmVmIikpKSl9fXJldHVybiBzfSwKTmQ6ZnVuY3Rpb24oYSl7dmFyIHMscgppZihhPT1udWxs
+KXM9bnVsbAplbHNle3M9SC5RSShbXSx0LmhoKQpmb3Iocj1KLklUKHQuUi5hKGEpKTtyLkYoKTspcy5w
+dXNoKFUuTmYoci5nbCgpKSl9cmV0dXJuIHN9LApOZjpmdW5jdGlvbihhKXt2YXIgcz1KLlU2KGEpLHI9
+SC5rKHMucShhLCJkZXNjcmlwdGlvbiIpKSxxPUguUUkoW10sdC5hSikKZm9yKHM9Si5JVCh0LlIuYShz
+LnEoYSwiZW50cmllcyIpKSk7cy5GKCk7KXEucHVzaChVLlJqKHMuZ2woKSkpCnJldHVybiBuZXcgVS55
+RChyLHEpfSwKUmo6ZnVuY3Rpb24oYSl7dmFyIHMscj1KLlU2KGEpLHE9SC5rKHIucShhLCJkZXNjcmlw
+dGlvbiIpKSxwPUguayhyLnEoYSwiZnVuY3Rpb24iKSksbz1yLnEoYSwibGluayIpCmlmKG89PW51bGwp
+bz1udWxsCmVsc2V7cz1KLlU2KG8pCm89bmV3IFUuTWwoSC5rKHMucShvLCJocmVmIikpLEguVWMocy5x
+KG8sImxpbmUiKSksSC5rKHMucShvLCJwYXRoIikpKX1yPXQuYk0uYShyLnEoYSwiaGludEFjdGlvbnMi
+KSkKcj1yPT1udWxsP251bGw6Si5NMShyLG5ldyBVLmFOKCksdC5KKS5icigwKQpyZXR1cm4gbmV3IFUu
+d2IocSxwLG8scj09bnVsbD9DLmRuOnIpfSwKZDI6ZnVuY3Rpb24gZDIoYSxiLGMsZCxlLGYpe3ZhciBf
+PXRoaXMKXy5hPWEKXy5iPWIKXy5jPWMKXy5kPWQKXy5lPWUKXy5mPWZ9LApTZTpmdW5jdGlvbiBTZShh
+LGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKTWw6ZnVuY3Rpb24gTWwoYSxiLGMpe3RoaXMuYT1hCnRoaXMu
+Yj1iCnRoaXMuYz1jfSwKeUQ6ZnVuY3Rpb24geUQoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sCndiOmZ1
+bmN0aW9uIHdiKGEsYixjLGQpe3ZhciBfPXRoaXMKXy5hPWEKXy5iPWIKXy5jPWMKXy5kPWR9LAphTjpm
+dW5jdGlvbiBhTigpe30sCmIwOmZ1bmN0aW9uIGIwKCl7fX0sVz17CngzOmZ1bmN0aW9uKCl7cmV0dXJu
+IHdpbmRvd30sClpyOmZ1bmN0aW9uKCl7cmV0dXJuIGRvY3VtZW50fSwKSjY6ZnVuY3Rpb24oYSl7dmFy
+IHM9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgiYSIpCmlmKGEhPW51bGwpQy54bi5zTFUocyxhKQpyZXR1
+cm4gc30sCkxqOmZ1bmN0aW9uKGEpe3JldHVybiBDU1MuZXNjYXBlKGEpfSwKVTk6ZnVuY3Rpb24oYSxi
+LGMpe3ZhciBzLHI9ZG9jdW1lbnQuYm9keQpyLnRvU3RyaW5nCnM9dC5hYwpzPW5ldyBILlU1KG5ldyBX
+LmU3KEMuUlkucjYocixhLGIsYykpLHMuQygiYTIobEQuRSkiKS5hKG5ldyBXLkN2KCkpLHMuQygiVTU8
+bEQuRT4iKSkKcmV0dXJuIHQuaC5hKHMuZ3I4KHMpKX0sCnJTOmZ1bmN0aW9uKGEpe3ZhciBzLHIscT0i
+ZWxlbWVudCB0YWcgdW5hdmFpbGFibGUiCnRyeXtzPUouWUUoYSkKcy5nbnMoYSkKcT1zLmducyhhKX1j
+YXRjaChyKXtILlJ1KHIpfXJldHVybiBxfSwKQzA6ZnVuY3Rpb24oYSxiKXthPWErYiY1MzY4NzA5MTEK
+YT1hKygoYSY1MjQyODcpPDwxMCkmNTM2ODcwOTExCnJldHVybiBhXmE+Pj42fSwKckU6ZnVuY3Rpb24o
+YSxiLGMsZCl7dmFyIHM9Vy5DMChXLkMwKFcuQzAoVy5DMCgwLGEpLGIpLGMpLGQpLHI9cysoKHMmNjcx
+MDg4NjMpPDwzKSY1MzY4NzA5MTEKcl49cj4+PjExCnJldHVybiByKygociYxNjM4Myk8PDE1KSY1MzY4
+NzA5MTF9LApUTjpmdW5jdGlvbihhLGIpe3ZhciBzLHIscT1hLmNsYXNzTGlzdApmb3Iocz1iLmxlbmd0
+aCxyPTA7cjxiLmxlbmd0aDtiLmxlbmd0aD09PXN8fCgwLEgubGspKGIpLCsrcilxLmFkZChiW3JdKX0s
+CkpFOmZ1bmN0aW9uKGEsYixjLGQsZSl7dmFyIHM9Vy5hRihuZXcgVy52TihjKSx0LkIpCmlmKHMhPW51
+bGwmJiEwKUouZFooYSxiLHMsITEpCnJldHVybiBuZXcgVy54QyhhLGIscywhMSxlLkMoInhDPDA+Iikp
+fSwKVHc6ZnVuY3Rpb24oYSl7dmFyIHM9Vy5KNihudWxsKSxyPXQuRi5hKHdpbmRvdy5sb2NhdGlvbikK
+cz1uZXcgVy5KUShuZXcgVy5tayhzLHIpKQpzLkNZKGEpCnJldHVybiBzfSwKcUQ6ZnVuY3Rpb24oYSxi
+LGMsZCl7dC5oLmEoYSkKSC5uKGIpCkgubihjKQp0LmNyLmEoZCkKcmV0dXJuITB9LApuWjpmdW5jdGlv
+bihhLGIsYyxkKXt2YXIgcyxyLHEKdC5oLmEoYSkKSC5uKGIpCkgubihjKQpzPXQuY3IuYShkKS5hCnI9
+cy5hCkMueG4uc0xVKHIsYykKcT1yLmhvc3RuYW1lCnM9cy5iCmlmKCEocT09cy5ob3N0bmFtZSYmci5w
+b3J0PT09cy5wb3J0JiZyLnByb3RvY29sPT09cy5wcm90b2NvbCkpaWYocT09PSIiKWlmKHIucG9ydD09
+PSIiKXtzPXIucHJvdG9jb2wKcz1zPT09IjoifHxzPT09IiJ9ZWxzZSBzPSExCmVsc2Ugcz0hMQplbHNl
+IHM9ITAKcmV0dXJuIHN9LApCbDpmdW5jdGlvbigpe3ZhciBzPXQuTixyPVAudE0oQy5ReCxzKSxxPXQu
+ZEcuYShuZXcgVy5JQSgpKSxwPUguUUkoWyJURU1QTEFURSJdLHQucykKcz1uZXcgVy5jdChyLFAuTHMo
+cyksUC5McyhzKSxQLkxzKHMpLG51bGwpCnMuQ1kobnVsbCxuZXcgSC5sSihDLlF4LHEsdC5EKSxwLG51
+bGwpCnJldHVybiBzfSwKcWM6ZnVuY3Rpb24oYSl7dmFyIHMKaWYoYT09bnVsbClyZXR1cm4gbnVsbApp
+ZigicG9zdE1lc3NhZ2UiIGluIGEpe3M9Vy5QMShhKQpyZXR1cm4gc31lbHNlIHJldHVybiB0LmNoLmEo
+YSl9LApQMTpmdW5jdGlvbihhKXtpZihhPT09d2luZG93KXJldHVybiB0LmNpLmEoYSkKZWxzZSByZXR1
+cm4gbmV3IFcuZFcoKX0sCmFGOmZ1bmN0aW9uKGEsYil7dmFyIHM9JC5YMwppZihzPT09Qy5OVSlyZXR1
+cm4gYQpyZXR1cm4gcy5QeShhLGIpfSwKcUU6ZnVuY3Rpb24gcUUoKXt9LApHaDpmdW5jdGlvbiBHaCgp
+e30sCmZZOmZ1bmN0aW9uIGZZKCl7fSwKclo6ZnVuY3Rpb24gclooKXt9LApBejpmdW5jdGlvbiBBeigp
+e30sClFQOmZ1bmN0aW9uIFFQKCl7fSwKbng6ZnVuY3Rpb24gbngoKXt9LApvSjpmdW5jdGlvbiBvSigp
+e30sCmlkOmZ1bmN0aW9uIGlkKCl7fSwKUUY6ZnVuY3Rpb24gUUYoKXt9LApOaDpmdW5jdGlvbiBOaCgp
+e30sCmFlOmZ1bmN0aW9uIGFlKCl7fSwKSUI6ZnVuY3Rpb24gSUIoKXt9LApuNzpmdW5jdGlvbiBuNygp
+e30sCnd6OmZ1bmN0aW9uIHd6KGEsYil7dGhpcy5hPWEKdGhpcy4kdGk9Yn0sCmN2OmZ1bmN0aW9uIGN2
+KCl7fSwKQ3Y6ZnVuY3Rpb24gQ3YoKXt9LAplYTpmdW5jdGlvbiBlYSgpe30sCkQwOmZ1bmN0aW9uIEQw
+KCl7fSwKaEg6ZnVuY3Rpb24gaEgoKXt9LApoNDpmdW5jdGlvbiBoNCgpe30sCmJyOmZ1bmN0aW9uIGJy
+KCl7fSwKVmI6ZnVuY3Rpb24gVmIoKXt9LApmSjpmdW5jdGlvbiBmSigpe30sCndhOmZ1bmN0aW9uIHdh
+KCl7fSwKU2c6ZnVuY3Rpb24gU2coKXt9LAp1ODpmdW5jdGlvbiB1OCgpe30sCkFqOmZ1bmN0aW9uIEFq
+KCl7fSwKZTc6ZnVuY3Rpb24gZTcoYSl7dGhpcy5hPWF9LApLVjpmdW5jdGlvbiBLVigpe30sCkJIOmZ1
+bmN0aW9uIEJIKCl7fSwKU046ZnVuY3Rpb24gU04oKXt9LApldzpmdW5jdGlvbiBldygpe30sCmxwOmZ1
+bmN0aW9uIGxwKCl7fSwKVGI6ZnVuY3Rpb24gVGIoKXt9LApJdjpmdW5jdGlvbiBJdigpe30sCldQOmZ1
+bmN0aW9uIFdQKCl7fSwKeVk6ZnVuY3Rpb24geVkoKXt9LAp3NjpmdW5jdGlvbiB3Nigpe30sCks1OmZ1
+bmN0aW9uIEs1KCl7fSwKQ206ZnVuY3Rpb24gQ20oKXt9LApDUTpmdW5jdGlvbiBDUSgpe30sCnc0OmZ1
+bmN0aW9uIHc0KCl7fSwKcmg6ZnVuY3Rpb24gcmgoKXt9LApjZjpmdW5jdGlvbiBjZigpe30sCmk3OmZ1
+bmN0aW9uIGk3KGEpe3RoaXMuYT1hfSwKU3k6ZnVuY3Rpb24gU3koYSl7dGhpcy5hPWF9LApLUzpmdW5j
+dGlvbiBLUyhhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKQTM6ZnVuY3Rpb24gQTMoYSxiKXt0aGlzLmE9
+YQp0aGlzLmI9Yn0sCkk0OmZ1bmN0aW9uIEk0KGEpe3RoaXMuYT1hfSwKRms6ZnVuY3Rpb24gRmsoYSxi
+KXt0aGlzLmE9YQp0aGlzLiR0aT1ifSwKUk86ZnVuY3Rpb24gUk8oYSxiLGMsZCl7dmFyIF89dGhpcwpf
+LmE9YQpfLmI9YgpfLmM9YwpfLiR0aT1kfSwKQ3E6ZnVuY3Rpb24gQ3EoYSxiLGMsZCl7dmFyIF89dGhp
+cwpfLmE9YQpfLmI9YgpfLmM9YwpfLiR0aT1kfSwKeEM6ZnVuY3Rpb24geEMoYSxiLGMsZCxlKXt2YXIg
+Xz10aGlzCl8uYj1hCl8uYz1iCl8uZD1jCl8uZT1kCl8uJHRpPWV9LAp2TjpmdW5jdGlvbiB2TihhKXt0
+aGlzLmE9YX0sCkpROmZ1bmN0aW9uIEpRKGEpe3RoaXMuYT1hfSwKR206ZnVuY3Rpb24gR20oKXt9LAp2
+RDpmdW5jdGlvbiB2RChhKXt0aGlzLmE9YX0sClV2OmZ1bmN0aW9uIFV2KGEpe3RoaXMuYT1hfSwKRWc6
+ZnVuY3Rpb24gRWcoYSxiLGMpe3RoaXMuYT1hCnRoaXMuYj1iCnRoaXMuYz1jfSwKbTY6ZnVuY3Rpb24g
+bTYoKXt9LApFbzpmdW5jdGlvbiBFbygpe30sCldrOmZ1bmN0aW9uIFdrKCl7fSwKY3Q6ZnVuY3Rpb24g
+Y3QoYSxiLGMsZCxlKXt2YXIgXz10aGlzCl8uZT1hCl8uYT1iCl8uYj1jCl8uYz1kCl8uZD1lfSwKSUE6
+ZnVuY3Rpb24gSUEoKXt9LApPdzpmdW5jdGlvbiBPdygpe30sClc5OmZ1bmN0aW9uIFc5KGEsYixjKXt2
+YXIgXz10aGlzCl8uYT1hCl8uYj1iCl8uYz0tMQpfLmQ9bnVsbApfLiR0aT1jfSwKZFc6ZnVuY3Rpb24g
+ZFcoKXt9LAptazpmdW5jdGlvbiBtayhhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKS286ZnVuY3Rpb24g
+S28oYSl7dGhpcy5hPWEKdGhpcy5iPTB9LApmbTpmdW5jdGlvbiBmbShhKXt0aGlzLmE9YX0sCkxlOmZ1
+bmN0aW9uIExlKCl7fSwKSzc6ZnVuY3Rpb24gSzcoKXt9LApyQjpmdW5jdGlvbiByQigpe30sClhXOmZ1
+bmN0aW9uIFhXKCl7fSwKb2E6ZnVuY3Rpb24gb2EoKXt9fSxYPXsKQ0w6ZnVuY3Rpb24oYSxiKXt2YXIg
+cyxyLHEscCxvLG49Yi54WihhKQpiLmhLKGEpCmlmKG4hPW51bGwpYT1DLnhCLnluKGEsbi5sZW5ndGgp
+CnM9dC5zCnI9SC5RSShbXSxzKQpxPUguUUkoW10scykKcz1hLmxlbmd0aAppZihzIT09MCYmYi5yNChD
+LnhCLlcoYSwwKSkpe2lmKDA+PXMpcmV0dXJuIEguT0goYSwwKQpDLk5tLmkocSxhWzBdKQpwPTF9ZWxz
+ZXtDLk5tLmkocSwiIikKcD0wfWZvcihvPXA7bzxzOysrbylpZihiLnI0KEMueEIuVyhhLG8pKSl7Qy5O
+bS5pKHIsQy54Qi5OaihhLHAsbykpCkMuTm0uaShxLGFbb10pCnA9bysxfWlmKHA8cyl7Qy5ObS5pKHIs
+Qy54Qi55bihhLHApKQpDLk5tLmkocSwiIil9cmV0dXJuIG5ldyBYLldEKGIsbixyLHEpfSwKV0Q6ZnVu
+Y3Rpb24gV0QoYSxiLGMsZCl7dmFyIF89dGhpcwpfLmE9YQpfLmI9YgpfLmQ9YwpfLmU9ZH0sCkk3OmZ1
+bmN0aW9uKGEpe3JldHVybiBuZXcgWC5kdihhKX0sCmR2OmZ1bmN0aW9uIGR2KGEpe3RoaXMuYT1hfX0K
+dmFyIHc9W0IsQyxELEUsRixILEosTCxNLE8sUCxSLFQsVSxXLFhdCmh1bmtIZWxwZXJzLnNldEZ1bmN0
+aW9uTmFtZXNJZk5lY2Vzc2FyeSh3KQp2YXIgJD17fQpILkZLLnByb3RvdHlwZT17fQpKLkd2LnByb3Rv
+dHlwZT17CkROOmZ1bmN0aW9uKGEsYil7cmV0dXJuIGE9PT1ifSwKZ2lPOmZ1bmN0aW9uKGEpe3JldHVy
+biBILmVRKGEpfSwKdzpmdW5jdGlvbihhKXtyZXR1cm4iSW5zdGFuY2Ugb2YgJyIrSC5saChhKSsiJyJ9
+LAplNzpmdW5jdGlvbihhLGIpe3Quby5hKGIpCnRocm93IEguYihQLmxyKGEsYi5nV2EoKSxiLmduZCgp
+LGIuZ1ZtKCkpKX19CkoueUUucHJvdG90eXBlPXsKdzpmdW5jdGlvbihhKXtyZXR1cm4gU3RyaW5nKGEp
+fSwKZ2lPOmZ1bmN0aW9uKGEpe3JldHVybiBhPzUxOTAxODoyMTgxNTl9LAokaWEyOjF9Ckoud2UucHJv
+dG90eXBlPXsKRE46ZnVuY3Rpb24oYSxiKXtyZXR1cm4gbnVsbD09Yn0sCnc6ZnVuY3Rpb24oYSl7cmV0
+dXJuIm51bGwifSwKZ2lPOmZ1bmN0aW9uKGEpe3JldHVybiAwfSwKJGljODoxfQpKLk1GLnByb3RvdHlw
+ZT17CmdpTzpmdW5jdGlvbihhKXtyZXR1cm4gMH0sCnc6ZnVuY3Rpb24oYSl7cmV0dXJuIFN0cmluZyhh
+KX0sCiRpdm06MX0KSi5pQy5wcm90b3R5cGU9e30KSi5rZC5wcm90b3R5cGU9e30KSi5jNS5wcm90b3R5
+cGU9ewp3OmZ1bmN0aW9uKGEpe3ZhciBzPWFbJC56KCldCmlmKHM9PW51bGwpcmV0dXJuIHRoaXMudChh
+KQpyZXR1cm4iSmF2YVNjcmlwdCBmdW5jdGlvbiBmb3IgIitILkVqKEoudyhzKSl9LAokaUVIOjF9Ckou
+amQucHJvdG90eXBlPXsKZHI6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gbmV3IEgualYoYSxILnQ2KGEpLkMo
+IkA8MT4iKS5LcShiKS5DKCJqVjwxLDI+IikpfSwKaTpmdW5jdGlvbihhLGIpe0gudDYoYSkuYy5hKGIp
+CmlmKCEhYS5maXhlZCRsZW5ndGgpSC52KFAuTDQoImFkZCIpKQphLnB1c2goYil9LApXNDpmdW5jdGlv
+bihhLGIpe3ZhciBzCmlmKCEhYS5maXhlZCRsZW5ndGgpSC52KFAuTDQoInJlbW92ZUF0IikpCnM9YS5s
+ZW5ndGgKaWYoYj49cyl0aHJvdyBILmIoUC5PNyhiLG51bGwpKQpyZXR1cm4gYS5zcGxpY2UoYiwxKVsw
+XX0sClVHOmZ1bmN0aW9uKGEsYixjKXt2YXIgcyxyCkgudDYoYSkuQygiY1g8MT4iKS5hKGMpCmlmKCEh
+YS5maXhlZCRsZW5ndGgpSC52KFAuTDQoImluc2VydEFsbCIpKQpQLndBKGIsMCxhLmxlbmd0aCwiaW5k
+ZXgiKQppZighdC5XLmIoYykpYz1KLlJYKGMpCnM9Si5IbShjKQphLmxlbmd0aD1hLmxlbmd0aCtzCnI9
+YitzCnRoaXMuWVcoYSxyLGEubGVuZ3RoLGEsYikKdGhpcy52ZyhhLGIscixjKX0sCkZWOmZ1bmN0aW9u
+KGEsYil7dmFyIHMKSC50NihhKS5DKCJjWDwxPiIpLmEoYikKaWYoISFhLmZpeGVkJGxlbmd0aClILnYo
+UC5MNCgiYWRkQWxsIikpCmlmKEFycmF5LmlzQXJyYXkoYikpe3RoaXMuS2goYSxiKQpyZXR1cm59Zm9y
+KHM9Si5JVChiKTtzLkYoKTspYS5wdXNoKHMuZ2woKSl9LApLaDpmdW5jdGlvbihhLGIpe3ZhciBzLHIK
+dC5iLmEoYikKcz1iLmxlbmd0aAppZihzPT09MClyZXR1cm4KaWYoYT09PWIpdGhyb3cgSC5iKFAuYTQo
+YSkpCmZvcihyPTA7cjxzOysrcilhLnB1c2goYltyXSl9LApFMjpmdW5jdGlvbihhLGIsYyl7dmFyIHM9
+SC50NihhKQpyZXR1cm4gbmV3IEgubEooYSxzLktxKGMpLkMoIjEoMikiKS5hKGIpLHMuQygiQDwxPiIp
+LktxKGMpLkMoImxKPDEsMj4iKSl9LAprOmZ1bmN0aW9uKGEsYil7dmFyIHMscj1QLk84KGEubGVuZ3Ro
+LCIiLCExLHQuTikKZm9yKHM9MDtzPGEubGVuZ3RoOysrcyl0aGlzLlk1KHIscyxILkVqKGFbc10pKQpy
+ZXR1cm4gci5qb2luKGIpfSwKZVI6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gSC5xQyhhLGIsbnVsbCxILnQ2
+KGEpLmMpfSwKTjA6ZnVuY3Rpb24oYSxiLGMsZCl7dmFyIHMscixxCmQuYShiKQpILnQ2KGEpLktxKGQp
+LkMoIjEoMSwyKSIpLmEoYykKcz1hLmxlbmd0aApmb3Iocj1iLHE9MDtxPHM7KytxKXtyPWMuJDIocixh
+W3FdKQppZihhLmxlbmd0aCE9PXMpdGhyb3cgSC5iKFAuYTQoYSkpfXJldHVybiByfSwKSHQ6ZnVuY3Rp
+b24oYSxiKXt2YXIgcyxyLHEscCxvLG49SC50NihhKQpuLkMoImEyKDEpIikuYShiKQpzPWEubGVuZ3Ro
+CmZvcihyPW51bGwscT0hMSxwPTA7cDxzOysrcCl7bz1hW3BdCmlmKEgub1QoYi4kMShvKSkpe2lmKHEp
+dGhyb3cgSC5iKEguQW0oKSkKcj1vCnE9ITB9aWYocyE9PWEubGVuZ3RoKXRocm93IEguYihQLmE0KGEp
+KX1pZihxKXJldHVybiBuLmMuYShyKQp0aHJvdyBILmIoSC5XcCgpKX0sCkU6ZnVuY3Rpb24oYSxiKXtp
+ZihiPDB8fGI+PWEubGVuZ3RoKXJldHVybiBILk9IKGEsYikKcmV0dXJuIGFbYl19LApndEg6ZnVuY3Rp
+b24oYSl7aWYoYS5sZW5ndGg+MClyZXR1cm4gYVswXQp0aHJvdyBILmIoSC5XcCgpKX0sCmdyWjpmdW5j
+dGlvbihhKXt2YXIgcz1hLmxlbmd0aAppZihzPjApcmV0dXJuIGFbcy0xXQp0aHJvdyBILmIoSC5XcCgp
+KX0sCllXOmZ1bmN0aW9uKGEsYixjLGQsZSl7dmFyIHMscixxLHAsbwpILnQ2KGEpLkMoImNYPDE+Iiku
+YShkKQppZighIWEuaW1tdXRhYmxlJGxpc3QpSC52KFAuTDQoInNldFJhbmdlIikpClAuakIoYixjLGEu
+bGVuZ3RoKQpzPWMtYgppZihzPT09MClyZXR1cm4KUC5rMShlLCJza2lwQ291bnQiKQppZih0LmouYihk
+KSl7cj1kCnE9ZX1lbHNle3I9Si5BNShkLGUpLnR0KDAsITEpCnE9MH1wPUouVTYocikKaWYocStzPnAu
+Z0EocikpdGhyb3cgSC5iKEguYXIoKSkKaWYocTxiKWZvcihvPXMtMTtvPj0wOy0tbylhW2Irb109cC5x
+KHIscStvKQplbHNlIGZvcihvPTA7bzxzOysrbylhW2Irb109cC5xKHIscStvKX0sCnZnOmZ1bmN0aW9u
+KGEsYixjLGQpe3JldHVybiB0aGlzLllXKGEsYixjLGQsMCl9LApWcjpmdW5jdGlvbihhLGIpe3ZhciBz
+LHIKSC50NihhKS5DKCJhMigxKSIpLmEoYikKcz1hLmxlbmd0aApmb3Iocj0wO3I8czsrK3Ipe2lmKEgu
+b1QoYi4kMShhW3JdKSkpcmV0dXJuITAKaWYoYS5sZW5ndGghPT1zKXRocm93IEguYihQLmE0KGEpKX1y
+ZXR1cm4hMX0sCnRnOmZ1bmN0aW9uKGEsYil7dmFyIHMKZm9yKHM9MDtzPGEubGVuZ3RoOysrcylpZihK
+LlJNKGFbc10sYikpcmV0dXJuITAKcmV0dXJuITF9LApnbDA6ZnVuY3Rpb24oYSl7cmV0dXJuIGEubGVu
+Z3RoPT09MH0sCmdvcjpmdW5jdGlvbihhKXtyZXR1cm4gYS5sZW5ndGghPT0wfSwKdzpmdW5jdGlvbihh
+KXtyZXR1cm4gUC54KGEsIlsiLCJdIil9LAp0dDpmdW5jdGlvbihhLGIpe3ZhciBzPUguUUkoYS5zbGlj
+ZSgwKSxILnQ2KGEpKQpyZXR1cm4gc30sCmJyOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLnR0KGEsITAp
+fSwKZ206ZnVuY3Rpb24oYSl7cmV0dXJuIG5ldyBKLm0xKGEsYS5sZW5ndGgsSC50NihhKS5DKCJtMTwx
+PiIpKX0sCmdpTzpmdW5jdGlvbihhKXtyZXR1cm4gSC5lUShhKX0sCmdBOmZ1bmN0aW9uKGEpe3JldHVy
+biBhLmxlbmd0aH0sCnNBOmZ1bmN0aW9uKGEsYil7aWYoISFhLmZpeGVkJGxlbmd0aClILnYoUC5MNCgi
+c2V0IGxlbmd0aCIpKQppZihiPDApdGhyb3cgSC5iKFAuVEUoYiwwLG51bGwsIm5ld0xlbmd0aCIsbnVs
+bCkpCmlmKGI+YS5sZW5ndGgpSC50NihhKS5jLmEobnVsbCkKYS5sZW5ndGg9Yn0sCnE6ZnVuY3Rpb24o
+YSxiKXtILklaKGIpCmlmKGI+PWEubGVuZ3RofHxiPDApdGhyb3cgSC5iKEgudShhLGIpKQpyZXR1cm4g
+YVtiXX0sClk1OmZ1bmN0aW9uKGEsYixjKXtILnQ2KGEpLmMuYShjKQppZighIWEuaW1tdXRhYmxlJGxp
+c3QpSC52KFAuTDQoImluZGV4ZWQgc2V0IikpCmlmKGI+PWEubGVuZ3RofHxiPDApdGhyb3cgSC5iKEgu
+dShhLGIpKQphW2JdPWN9LAokaWJROjEsCiRpY1g6MSwKJGl6TToxfQpKLlBvLnByb3RvdHlwZT17fQpK
+Lm0xLnByb3RvdHlwZT17CmdsOmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuJHRpLmMuYSh0aGlzLmQpfSwK
+RjpmdW5jdGlvbigpe3ZhciBzLHI9dGhpcyxxPXIuYSxwPXEubGVuZ3RoCmlmKHIuYiE9PXApdGhyb3cg
+SC5iKEgubGsocSkpCnM9ci5jCmlmKHM+PXApe3Iuc00obnVsbCkKcmV0dXJuITF9ci5zTShxW3NdKTsr
+K3IuYwpyZXR1cm4hMH0sCnNNOmZ1bmN0aW9uKGEpe3RoaXMuZD10aGlzLiR0aS5DKCIxPyIpLmEoYSl9
+LAokaUFuOjF9CkoucUkucHJvdG90eXBlPXsKelE6ZnVuY3Rpb24oYSl7aWYoYT4wKXtpZihhIT09MS8w
+KXJldHVybiBNYXRoLnJvdW5kKGEpfWVsc2UgaWYoYT4tMS8wKXJldHVybiAwLU1hdGgucm91bmQoMC1h
+KQp0aHJvdyBILmIoUC5MNCgiIithKyIucm91bmQoKSIpKX0sCnc6ZnVuY3Rpb24oYSl7aWYoYT09PTAm
+JjEvYTwwKXJldHVybiItMC4wIgplbHNlIHJldHVybiIiK2F9LApnaU86ZnVuY3Rpb24oYSl7dmFyIHMs
+cixxLHAsbz1hfDAKaWYoYT09PW8pcmV0dXJuIG8mNTM2ODcwOTExCnM9TWF0aC5hYnMoYSkKcj1NYXRo
+LmxvZyhzKS8wLjY5MzE0NzE4MDU1OTk0NTN8MApxPU1hdGgucG93KDIscikKcD1zPDE/cy9xOnEvcwpy
+ZXR1cm4oKHAqOTAwNzE5OTI1NDc0MDk5MnwwKSsocCozNTQyMjQzMTgxMTc2NTIxfDApKSo1OTkxOTcr
+cioxMjU5JjUzNjg3MDkxMX0sCnpZOmZ1bmN0aW9uKGEsYil7dmFyIHM9YSViCmlmKHM9PT0wKXJldHVy
+biAwCmlmKHM+MClyZXR1cm4gcwpyZXR1cm4gcytifSwKQlU6ZnVuY3Rpb24oYSxiKXtyZXR1cm4oYXww
+KT09PWE/YS9ifDA6dGhpcy5ESihhLGIpfSwKREo6ZnVuY3Rpb24oYSxiKXt2YXIgcz1hL2IKaWYocz49
+LTIxNDc0ODM2NDgmJnM8PTIxNDc0ODM2NDcpcmV0dXJuIHN8MAppZihzPjApe2lmKHMhPT0xLzApcmV0
+dXJuIE1hdGguZmxvb3Iocyl9ZWxzZSBpZihzPi0xLzApcmV0dXJuIE1hdGguY2VpbChzKQp0aHJvdyBI
+LmIoUC5MNCgiUmVzdWx0IG9mIHRydW5jYXRpbmcgZGl2aXNpb24gaXMgIitILkVqKHMpKyI6ICIrSC5F
+aihhKSsiIH4vICIrYikpfSwKd0c6ZnVuY3Rpb24oYSxiKXt2YXIgcwppZihhPjApcz10aGlzLnAzKGEs
+YikKZWxzZXtzPWI+MzE/MzE6YgpzPWE+PnM+Pj4wfXJldHVybiBzfSwKYmY6ZnVuY3Rpb24oYSxiKXtp
+ZigwPmIpdGhyb3cgSC5iKEgudEwoYikpCnJldHVybiB0aGlzLnAzKGEsYil9LApwMzpmdW5jdGlvbihh
+LGIpe3JldHVybiBiPjMxPzA6YT4+PmJ9LAokaUNQOjEsCiRpWlo6MX0KSi5iVS5wcm90b3R5cGU9eyRp
+SWo6MX0KSi5rRC5wcm90b3R5cGU9e30KSi5Eci5wcm90b3R5cGU9ewpPOmZ1bmN0aW9uKGEsYil7aWYo
+YjwwKXRocm93IEguYihILnUoYSxiKSkKaWYoYj49YS5sZW5ndGgpSC52KEgudShhLGIpKQpyZXR1cm4g
+YS5jaGFyQ29kZUF0KGIpfSwKVzpmdW5jdGlvbihhLGIpe2lmKGI+PWEubGVuZ3RoKXRocm93IEguYihI
+LnUoYSxiKSkKcmV0dXJuIGEuY2hhckNvZGVBdChiKX0sCmRkOmZ1bmN0aW9uKGEsYil7cmV0dXJuIG5l
+dyBILnVuKGIsYSwwKX0sCmg6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gYStifSwKVGM6ZnVuY3Rpb24oYSxi
+KXt2YXIgcz1iLmxlbmd0aCxyPWEubGVuZ3RoCmlmKHM+cilyZXR1cm4hMQpyZXR1cm4gYj09PXRoaXMu
+eW4oYSxyLXMpfSwKaTc6ZnVuY3Rpb24oYSxiLGMsZCl7dmFyIHM9UC5qQihiLGMsYS5sZW5ndGgpLHI9
+YS5zdWJzdHJpbmcoMCxiKSxxPWEuc3Vic3RyaW5nKHMpCnJldHVybiByK2QrcX0sClFpOmZ1bmN0aW9u
+KGEsYixjKXt2YXIgcwppZihjPDB8fGM+YS5sZW5ndGgpdGhyb3cgSC5iKFAuVEUoYywwLGEubGVuZ3Ro
+LG51bGwsbnVsbCkpCnM9YytiLmxlbmd0aAppZihzPmEubGVuZ3RoKXJldHVybiExCnJldHVybiBiPT09
+YS5zdWJzdHJpbmcoYyxzKX0sCm46ZnVuY3Rpb24oYSxiKXtyZXR1cm4gdGhpcy5RaShhLGIsMCl9LApO
ajpmdW5jdGlvbihhLGIsYyl7aWYoYz09bnVsbCljPWEubGVuZ3RoCmlmKGI8MCl0aHJvdyBILmIoUC5P
NyhiLG51bGwpKQppZihiPmMpdGhyb3cgSC5iKFAuTzcoYixudWxsKSkKaWYoYz5hLmxlbmd0aCl0aHJv
dyBILmIoUC5PNyhjLG51bGwpKQpyZXR1cm4gYS5zdWJzdHJpbmcoYixjKX0sCnluOmZ1bmN0aW9uKGEs
@@ -9654,2203 +9621,2192 @@
LHIscQpmb3Iocz1hLmxlbmd0aCxyPTAscT0wO3E8czsrK3Epe3I9cithLmNoYXJDb2RlQXQocSkmNTM2
ODcwOTExCnI9cisoKHImNTI0Mjg3KTw8MTApJjUzNjg3MDkxMQpyXj1yPj42fXI9cisoKHImNjcxMDg4
NjMpPDwzKSY1MzY4NzA5MTEKcl49cj4+MTEKcmV0dXJuIHIrKChyJjE2MzgzKTw8MTUpJjUzNjg3MDkx
-MX0sCmdBOmZ1bmN0aW9uKGEpe3JldHVybiBhLmxlbmd0aH0sCnE6ZnVuY3Rpb24oYSxiKXtILnVQKGIp
+MX0sCmdBOmZ1bmN0aW9uKGEpe3JldHVybiBhLmxlbmd0aH0sCnE6ZnVuY3Rpb24oYSxiKXtILklaKGIp
CmlmKGI+PWEubGVuZ3RofHwhMSl0aHJvdyBILmIoSC51KGEsYikpCnJldHVybiBhW2JdfSwKJGl2WDox
LAokaXFVOjF9CkguQlIucHJvdG90eXBlPXsKZ206ZnVuY3Rpb24oYSl7dmFyIHM9SC5MaCh0aGlzKQpy
-ZXR1cm4gbmV3IEguRTcoSi5JVCh0aGlzLmdPTigpKSxzLkMoIkA8MT4iKS5LcShzLlFbMV0pLkMoIkU3
+ZXR1cm4gbmV3IEguZVQoSi5JVCh0aGlzLmdPTigpKSxzLkMoIkA8MT4iKS5LcShzLlFbMV0pLkMoImVU
PDEsMj4iKSl9LApnQTpmdW5jdGlvbihhKXtyZXR1cm4gSi5IbSh0aGlzLmdPTigpKX0sCmdsMDpmdW5j
dGlvbihhKXtyZXR1cm4gSi51VSh0aGlzLmdPTigpKX0sCmdvcjpmdW5jdGlvbihhKXtyZXR1cm4gSi5G
Nyh0aGlzLmdPTigpKX0sCmVSOmZ1bmN0aW9uKGEsYil7dmFyIHM9SC5MaCh0aGlzKQpyZXR1cm4gSC5H
SihKLkE1KHRoaXMuZ09OKCksYikscy5jLHMuUVsxXSl9LApFOmZ1bmN0aW9uKGEsYil7cmV0dXJuIEgu
TGgodGhpcykuUVsxXS5hKEouR0EodGhpcy5nT04oKSxiKSl9LAp3OmZ1bmN0aW9uKGEpe3JldHVybiBK
-LncodGhpcy5nT04oKSl9fQpILkU3LnByb3RvdHlwZT17CkY6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5h
+LncodGhpcy5nT04oKSl9fQpILmVULnByb3RvdHlwZT17CkY6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5h
LkYoKX0sCmdsOmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuJHRpLlFbMV0uYSh0aGlzLmEuZ2woKSl9LAok
aUFuOjF9CkguWnkucHJvdG90eXBlPXsKZ09OOmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuYX19Ckgub2wu
cHJvdG90eXBlPXskaWJROjF9CkguVXEucHJvdG90eXBlPXsKcTpmdW5jdGlvbihhLGIpe3JldHVybiB0
-aGlzLiR0aS5RWzFdLmEoSi54OSh0aGlzLmEsSC51UChiKSkpfSwKWTU6ZnVuY3Rpb24oYSxiLGMpe3Zh
+aGlzLiR0aS5RWzFdLmEoSi54OSh0aGlzLmEsSC5JWihiKSkpfSwKWTU6ZnVuY3Rpb24oYSxiLGMpe3Zh
ciBzPXRoaXMuJHRpCkoudTkodGhpcy5hLGIscy5jLmEocy5RWzFdLmEoYykpKX0sCiRpYlE6MSwKJGl6
TToxfQpILmpWLnByb3RvdHlwZT17CmRyOmZ1bmN0aW9uKGEsYil7cmV0dXJuIG5ldyBILmpWKHRoaXMu
YSx0aGlzLiR0aS5DKCJAPDE+IikuS3EoYikuQygialY8MSwyPiIpKX0sCmdPTjpmdW5jdGlvbigpe3Jl
-dHVybiB0aGlzLmF9fQpILm4ucHJvdG90eXBlPXsKdzpmdW5jdGlvbihhKXt2YXIgcz0iTGF0ZUluaXRp
-YWxpemF0aW9uRXJyb3I6ICIrdGhpcy5hCnJldHVybiBzfX0KSC5yMy5wcm90b3R5cGU9ewp3OmZ1bmN0
-aW9uKGEpe3ZhciBzPSJSZWFjaGFiaWxpdHlFcnJvcjogIit0aGlzLmEKcmV0dXJuIHN9fQpILnFqLnBy
-b3RvdHlwZT17CmdBOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmEubGVuZ3RofSwKcTpmdW5jdGlvbihh
-LGIpe3JldHVybiBDLnhCLk8odGhpcy5hLEgudVAoYikpfX0KSC5HTS5wcm90b3R5cGU9ewp3OmZ1bmN0
-aW9uKGEpe3JldHVybiJOdWxsIGlzIG5vdCBhIHZhbGlkIHZhbHVlIGZvciB0aGUgcGFyYW1ldGVyICci
-K3RoaXMuYSsiJyBvZiB0eXBlICciK0guS3godGhpcy4kdGkuYykudygwKSsiJyJ9fQpILmJRLnByb3Rv
-dHlwZT17fQpILmFMLnByb3RvdHlwZT17CmdtOmZ1bmN0aW9uKGEpe3ZhciBzPXRoaXMKcmV0dXJuIG5l
-dyBILmE3KHMscy5nQShzKSxILkxoKHMpLkMoImE3PGFMLkU+IikpfSwKZ2wwOmZ1bmN0aW9uKGEpe3Jl
-dHVybiB0aGlzLmdBKHRoaXMpPT09MH0sCms6ZnVuY3Rpb24oYSxiKXt2YXIgcyxyLHEscD10aGlzLG89
-cC5nQShwKQppZihiLmxlbmd0aCE9PTApe2lmKG89PT0wKXJldHVybiIiCnM9SC5FaihwLkUoMCwwKSkK
-aWYobyE9PXAuZ0EocCkpdGhyb3cgSC5iKFAuYTQocCkpCmZvcihyPXMscT0xO3E8bzsrK3Epe3I9citi
-K0guRWoocC5FKDAscSkpCmlmKG8hPT1wLmdBKHApKXRocm93IEguYihQLmE0KHApKX1yZXR1cm4gci5j
-aGFyQ29kZUF0KDApPT0wP3I6cn1lbHNle2ZvcihxPTAscj0iIjtxPG87KytxKXtyKz1ILkVqKHAuRSgw
-LHEpKQppZihvIT09cC5nQShwKSl0aHJvdyBILmIoUC5hNChwKSl9cmV0dXJuIHIuY2hhckNvZGVBdCgw
-KT09MD9yOnJ9fSwKZXY6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gdGhpcy5HRygwLEguTGgodGhpcykuQygi
-YTIoYUwuRSkiKS5hKGIpKX0sCkUyOmZ1bmN0aW9uKGEsYixjKXt2YXIgcz1ILkxoKHRoaXMpCnJldHVy
-biBuZXcgSC5sSih0aGlzLHMuS3EoYykuQygiMShhTC5FKSIpLmEoYikscy5DKCJAPGFMLkU+IikuS3Eo
-YykuQygibEo8MSwyPiIpKX0sCmVSOmZ1bmN0aW9uKGEsYil7cmV0dXJuIEgucUModGhpcyxiLG51bGws
-SC5MaCh0aGlzKS5DKCJhTC5FIikpfSwKdHQ6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gUC5ZMSh0aGlzLCEw
-LEguTGgodGhpcykuQygiYUwuRSIpKX0sCmJyOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLnR0KGEsITAp
-fX0KSC5uSC5wcm90b3R5cGU9ewpIZDpmdW5jdGlvbihhLGIsYyxkKXt2YXIgcyxyPXRoaXMuYgpQLmsx
-KHIsInN0YXJ0IikKcz10aGlzLmMKaWYocyE9bnVsbCl7UC5rMShzLCJlbmQiKQppZihyPnMpdGhyb3cg
-SC5iKFAuVEUociwwLHMsInN0YXJ0IixudWxsKSl9fSwKZ1VEOmZ1bmN0aW9uKCl7dmFyIHM9Si5IbSh0
-aGlzLmEpLHI9dGhpcy5jCmlmKHI9PW51bGx8fHI+cylyZXR1cm4gcwpyZXR1cm4gcn0sCmdBczpmdW5j
-dGlvbigpe3ZhciBzPUouSG0odGhpcy5hKSxyPXRoaXMuYgppZihyPnMpcmV0dXJuIHMKcmV0dXJuIHJ9
-LApnQTpmdW5jdGlvbihhKXt2YXIgcyxyPUouSG0odGhpcy5hKSxxPXRoaXMuYgppZihxPj1yKXJldHVy
-biAwCnM9dGhpcy5jCmlmKHM9PW51bGx8fHM+PXIpcmV0dXJuIHItcQppZih0eXBlb2YgcyE9PSJudW1i
-ZXIiKXJldHVybiBzLkhOKCkKcmV0dXJuIHMtcX0sCkU6ZnVuY3Rpb24oYSxiKXt2YXIgcz10aGlzLHI9
-cy5nQXMoKStiCmlmKGI8MHx8cj49cy5nVUQoKSl0aHJvdyBILmIoUC5DZihiLHMsImluZGV4IixudWxs
-LG51bGwpKQpyZXR1cm4gSi5HQShzLmEscil9LAplUjpmdW5jdGlvbihhLGIpe3ZhciBzLHIscT10aGlz
-ClAuazEoYiwiY291bnQiKQpzPXEuYitiCnI9cS5jCmlmKHIhPW51bGwmJnM+PXIpcmV0dXJuIG5ldyBI
-Lk1CKHEuJHRpLkMoIk1CPDE+IikpCnJldHVybiBILnFDKHEuYSxzLHIscS4kdGkuYyl9LAp0dDpmdW5j
-dGlvbihhLGIpe3ZhciBzLHIscSxwPXRoaXMsbz1wLmIsbj1wLmEsbT1KLlU2KG4pLGw9bS5nQShuKSxr
-PXAuYwppZihrIT1udWxsJiZrPGwpbD1rCmlmKHR5cGVvZiBsIT09Im51bWJlciIpcmV0dXJuIGwuSE4o
-KQpzPWwtbwppZihzPD0wKXtuPUouUWkoMCxwLiR0aS5jKQpyZXR1cm4gbn1yPVAuTzgocyxtLkUobixv
-KSwhMSxwLiR0aS5jKQpmb3IocT0xO3E8czsrK3Epe0MuTm0uWTUocixxLG0uRShuLG8rcSkpCmlmKG0u
-Z0Eobik8bCl0aHJvdyBILmIoUC5hNChwKSl9cmV0dXJuIHJ9fQpILmE3LnByb3RvdHlwZT17CmdsOmZ1
-bmN0aW9uKCl7cmV0dXJuIHRoaXMuZH0sCkY6ZnVuY3Rpb24oKXt2YXIgcyxyPXRoaXMscT1yLmEscD1K
-LlU2KHEpLG89cC5nQShxKQppZihyLmIhPT1vKXRocm93IEguYihQLmE0KHEpKQpzPXIuYwppZihzPj1v
-KXtyLnNJKG51bGwpCnJldHVybiExfXIuc0kocC5FKHEscykpOysrci5jCnJldHVybiEwfSwKc0k6ZnVu
-Y3Rpb24oYSl7dGhpcy5kPXRoaXMuJHRpLkMoIjE/IikuYShhKX0sCiRpQW46MX0KSC5pMS5wcm90b3R5
-cGU9ewpnbTpmdW5jdGlvbihhKXt2YXIgcz1ILkxoKHRoaXMpCnJldHVybiBuZXcgSC5NSChKLklUKHRo
-aXMuYSksdGhpcy5iLHMuQygiQDwxPiIpLktxKHMuUVsxXSkuQygiTUg8MSwyPiIpKX0sCmdBOmZ1bmN0
-aW9uKGEpe3JldHVybiBKLkhtKHRoaXMuYSl9LApnbDA6ZnVuY3Rpb24oYSl7cmV0dXJuIEoudVUodGhp
-cy5hKX0sCkU6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gdGhpcy5iLiQxKEouR0EodGhpcy5hLGIpKX19Ckgu
-eHkucHJvdG90eXBlPXskaWJROjF9CkguTUgucHJvdG90eXBlPXsKRjpmdW5jdGlvbigpe3ZhciBzPXRo
-aXMscj1zLmIKaWYoci5GKCkpe3Muc0kocy5jLiQxKHIuZ2woKSkpCnJldHVybiEwfXMuc0kobnVsbCkK
-cmV0dXJuITF9LApnbDpmdW5jdGlvbigpe3JldHVybiB0aGlzLmF9LApzSTpmdW5jdGlvbihhKXt0aGlz
-LmE9dGhpcy4kdGkuQygiMj8iKS5hKGEpfX0KSC5sSi5wcm90b3R5cGU9ewpnQTpmdW5jdGlvbihhKXty
-ZXR1cm4gSi5IbSh0aGlzLmEpfSwKRTpmdW5jdGlvbihhLGIpe3JldHVybiB0aGlzLmIuJDEoSi5HQSh0
-aGlzLmEsYikpfX0KSC5VNS5wcm90b3R5cGU9ewpnbTpmdW5jdGlvbihhKXtyZXR1cm4gbmV3IEguU08o
-Si5JVCh0aGlzLmEpLHRoaXMuYix0aGlzLiR0aS5DKCJTTzwxPiIpKX19CkguU08ucHJvdG90eXBlPXsK
-RjpmdW5jdGlvbigpe3ZhciBzLHIKZm9yKHM9dGhpcy5hLHI9dGhpcy5iO3MuRigpOylpZihILm9UKHIu
-JDEocy5nbCgpKSkpcmV0dXJuITAKcmV0dXJuITF9LApnbDpmdW5jdGlvbigpe3JldHVybiB0aGlzLmEu
-Z2woKX19CkguQU0ucHJvdG90eXBlPXsKZVI6ZnVuY3Rpb24oYSxiKXtQLmsxKGIsImNvdW50IikKcmV0
-dXJuIG5ldyBILkFNKHRoaXMuYSx0aGlzLmIrYixILkxoKHRoaXMpLkMoIkFNPDE+IikpfSwKZ206ZnVu
-Y3Rpb24oYSl7cmV0dXJuIG5ldyBILlUxKEouSVQodGhpcy5hKSx0aGlzLmIsSC5MaCh0aGlzKS5DKCJV
-MTwxPiIpKX19CkguZDUucHJvdG90eXBlPXsKZ0E6ZnVuY3Rpb24oYSl7dmFyIHM9Si5IbSh0aGlzLmEp
-LXRoaXMuYgppZihzPj0wKXJldHVybiBzCnJldHVybiAwfSwKZVI6ZnVuY3Rpb24oYSxiKXtQLmsxKGIs
-ImNvdW50IikKcmV0dXJuIG5ldyBILmQ1KHRoaXMuYSx0aGlzLmIrYix0aGlzLiR0aSl9LAokaWJROjF9
-CkguVTEucHJvdG90eXBlPXsKRjpmdW5jdGlvbigpe3ZhciBzLHIKZm9yKHM9dGhpcy5hLHI9MDtyPHRo
-aXMuYjsrK3Ipcy5GKCkKdGhpcy5iPTAKcmV0dXJuIHMuRigpfSwKZ2w6ZnVuY3Rpb24oKXtyZXR1cm4g
-dGhpcy5hLmdsKCl9fQpILk1CLnByb3RvdHlwZT17CmdtOmZ1bmN0aW9uKGEpe3JldHVybiBDLkd3fSwK
-Z2wwOmZ1bmN0aW9uKGEpe3JldHVybiEwfSwKZ0E6ZnVuY3Rpb24oYSl7cmV0dXJuIDB9LApFOmZ1bmN0
-aW9uKGEsYil7dGhyb3cgSC5iKFAuVEUoYiwwLDAsImluZGV4IixudWxsKSl9LAplUjpmdW5jdGlvbihh
-LGIpe1AuazEoYiwiY291bnQiKQpyZXR1cm4gdGhpc319CkguRnUucHJvdG90eXBlPXsKRjpmdW5jdGlv
-bigpe3JldHVybiExfSwKZ2w6ZnVuY3Rpb24oKXt0aHJvdyBILmIoSC5XcCgpKX0sCiRpQW46MX0KSC51
-Ni5wcm90b3R5cGU9ewpnbTpmdW5jdGlvbihhKXtyZXR1cm4gbmV3IEguSkIoSi5JVCh0aGlzLmEpLHRo
-aXMuJHRpLkMoIkpCPDE+IikpfX0KSC5KQi5wcm90b3R5cGU9ewpGOmZ1bmN0aW9uKCl7dmFyIHMscgpm
-b3Iocz10aGlzLmEscj10aGlzLiR0aS5jO3MuRigpOylpZihyLmIocy5nbCgpKSlyZXR1cm4hMApyZXR1
-cm4hMX0sCmdsOmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuJHRpLmMuYSh0aGlzLmEuZ2woKSl9LAokaUFu
-OjF9CkguU1UucHJvdG90eXBlPXt9CkguUmUucHJvdG90eXBlPXsKWTU6ZnVuY3Rpb24oYSxiLGMpe0gu
-TGgodGhpcykuQygiUmUuRSIpLmEoYykKdGhyb3cgSC5iKFAuTDQoIkNhbm5vdCBtb2RpZnkgYW4gdW5t
-b2RpZmlhYmxlIGxpc3QiKSl9fQpILncyLnByb3RvdHlwZT17fQpILnd2LnByb3RvdHlwZT17CmdpTzpm
-dW5jdGlvbihhKXt2YXIgcz10aGlzLl9oYXNoQ29kZQppZihzIT1udWxsKXJldHVybiBzCnM9NjY0NTk3
-KkouaGYodGhpcy5hKSY1MzY4NzA5MTEKdGhpcy5faGFzaENvZGU9cwpyZXR1cm4gc30sCnc6ZnVuY3Rp
-b24oYSl7cmV0dXJuJ1N5bWJvbCgiJytILkVqKHRoaXMuYSkrJyIpJ30sCkROOmZ1bmN0aW9uKGEsYil7
-aWYoYj09bnVsbClyZXR1cm4hMQpyZXR1cm4gYiBpbnN0YW5jZW9mIEgud3YmJnRoaXMuYT09Yi5hfSwK
-JGlHRDoxfQpILlFDLnByb3RvdHlwZT17fQpILlBELnByb3RvdHlwZT17fQpILldVLnByb3RvdHlwZT17
-CmdsMDpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5nQSh0aGlzKT09PTB9LAp3OmZ1bmN0aW9uKGEpe3Jl
-dHVybiBQLm5PKHRoaXMpfSwKWTU6ZnVuY3Rpb24oYSxiLGMpe3ZhciBzPUguTGgodGhpcykKcy5jLmEo
-YikKcy5RWzFdLmEoYykKSC5kYygpCkguQmkodS5nKX0sCmdQdTpmdW5jdGlvbihhKXtyZXR1cm4gdGhp
-cy5xNCgwLEguTGgodGhpcykuQygiTjM8MSwyPiIpKX0sCnE0OmZ1bmN0aW9uKGEsYil7dmFyIHM9dGhp
-cwpyZXR1cm4gUC5sMChmdW5jdGlvbigpe3ZhciByPWEKdmFyIHE9MCxwPTEsbyxuLG0sbCxrCnJldHVy
-biBmdW5jdGlvbiAkYXN5bmMkZ1B1KGMsZCl7aWYoYz09PTEpe289ZApxPXB9d2hpbGUodHJ1ZSlzd2l0
-Y2gocSl7Y2FzZSAwOm49cy5ndmMoKSxuPW4uZ20obiksbT1ILkxoKHMpLG09bS5DKCJAPDE+IikuS3Eo
-bS5RWzFdKS5DKCJOMzwxLDI+IikKY2FzZSAyOmlmKCFuLkYoKSl7cT0zCmJyZWFrfWw9bi5nbCgpCms9
-cy5xKDAsbCkKay50b1N0cmluZwpxPTQKcmV0dXJuIG5ldyBQLk4zKGwsayxtKQpjYXNlIDQ6cT0yCmJy
-ZWFrCmNhc2UgMzpyZXR1cm4gUC5UaCgpCmNhc2UgMTpyZXR1cm4gUC5ZbShvKX19fSxiKX0sCiRpWjA6
-MX0KSC5MUC5wcm90b3R5cGU9ewpnQTpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5hfSwKeDQ6ZnVuY3Rp
-b24oYSl7aWYodHlwZW9mIGEhPSJzdHJpbmciKXJldHVybiExCmlmKCJfX3Byb3RvX18iPT09YSlyZXR1
-cm4hMQpyZXR1cm4gdGhpcy5iLmhhc093blByb3BlcnR5KGEpfSwKcTpmdW5jdGlvbihhLGIpe2lmKCF0
-aGlzLng0KGIpKXJldHVybiBudWxsCnJldHVybiB0aGlzLnFQKGIpfSwKcVA6ZnVuY3Rpb24oYSl7cmV0
-dXJuIHRoaXMuYltILmgoYSldfSwKSzpmdW5jdGlvbihhLGIpe3ZhciBzLHIscSxwLG89SC5MaCh0aGlz
-KQpvLkMoIn4oMSwyKSIpLmEoYikKcz10aGlzLmMKZm9yKHI9cy5sZW5ndGgsbz1vLlFbMV0scT0wO3E8
-cjsrK3Epe3A9c1txXQpiLiQyKHAsby5hKHRoaXMucVAocCkpKX19LApndmM6ZnVuY3Rpb24oKXtyZXR1
-cm4gbmV3IEguWFIodGhpcyxILkxoKHRoaXMpLkMoIlhSPDE+IikpfX0KSC5YUi5wcm90b3R5cGU9ewpn
-bTpmdW5jdGlvbihhKXt2YXIgcz10aGlzLmEuYwpyZXR1cm4gbmV3IEoubTEocyxzLmxlbmd0aCxILnQ2
-KHMpLkMoIm0xPDE+IikpfSwKZ0E6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuYS5jLmxlbmd0aH19Ckgu
-TEkucHJvdG90eXBlPXsKZ1dhOmZ1bmN0aW9uKCl7dmFyIHM9dGhpcy5hCnJldHVybiBzfSwKZ25kOmZ1
-bmN0aW9uKCl7dmFyIHMscixxLHAsbz10aGlzCmlmKG8uYz09PTEpcmV0dXJuIEMuaFUKcz1vLmQKcj1z
-Lmxlbmd0aC1vLmUubGVuZ3RoLW8uZgppZihyPT09MClyZXR1cm4gQy5oVQpxPVtdCmZvcihwPTA7cDxy
-OysrcCl7aWYocD49cy5sZW5ndGgpcmV0dXJuIEguT0gocyxwKQpxLnB1c2goc1twXSl9cmV0dXJuIEou
-ekMocSl9LApnVm06ZnVuY3Rpb24oKXt2YXIgcyxyLHEscCxvLG4sbSxsLGs9dGhpcwppZihrLmMhPT0w
-KXJldHVybiBDLldPCnM9ay5lCnI9cy5sZW5ndGgKcT1rLmQKcD1xLmxlbmd0aC1yLWsuZgppZihyPT09
-MClyZXR1cm4gQy5XTwpvPW5ldyBILk41KHQuZW8pCmZvcihuPTA7bjxyOysrbil7aWYobj49cy5sZW5n
-dGgpcmV0dXJuIEguT0gocyxuKQptPXNbbl0KbD1wK24KaWYobDwwfHxsPj1xLmxlbmd0aClyZXR1cm4g
-SC5PSChxLGwpCm8uWTUoMCxuZXcgSC53dihtKSxxW2xdKX1yZXR1cm4gbmV3IEguUEQobyx0LmdGKX0s
-CiRpdlE6MX0KSC5Dai5wcm90b3R5cGU9ewokMjpmdW5jdGlvbihhLGIpe3ZhciBzCkguaChhKQpzPXRo
-aXMuYQpzLmI9cy5iKyIkIitILkVqKGEpCkMuTm0uaSh0aGlzLmIsYSkKQy5ObS5pKHRoaXMuYyxiKTsr
-K3MuYX0sCiRTOjEzfQpILmY5LnByb3RvdHlwZT17CnFTOmZ1bmN0aW9uKGEpe3ZhciBzLHIscT10aGlz
-LHA9bmV3IFJlZ0V4cChxLmEpLmV4ZWMoYSkKaWYocD09bnVsbClyZXR1cm4gbnVsbApzPU9iamVjdC5j
-cmVhdGUobnVsbCkKcj1xLmIKaWYociE9PS0xKXMuYXJndW1lbnRzPXBbcisxXQpyPXEuYwppZihyIT09
-LTEpcy5hcmd1bWVudHNFeHByPXBbcisxXQpyPXEuZAppZihyIT09LTEpcy5leHByPXBbcisxXQpyPXEu
-ZQppZihyIT09LTEpcy5tZXRob2Q9cFtyKzFdCnI9cS5mCmlmKHIhPT0tMSlzLnJlY2VpdmVyPXBbcisx
-XQpyZXR1cm4gc319CkguVzAucHJvdG90eXBlPXsKdzpmdW5jdGlvbihhKXt2YXIgcz10aGlzLmIKaWYo
-cz09bnVsbClyZXR1cm4iTm9TdWNoTWV0aG9kRXJyb3I6ICIrSC5Faih0aGlzLmEpCnJldHVybiJOb1N1
-Y2hNZXRob2RFcnJvcjogbWV0aG9kIG5vdCBmb3VuZDogJyIrcysiJyBvbiBudWxsIn19CkguYXoucHJv
-dG90eXBlPXsKdzpmdW5jdGlvbihhKXt2YXIgcyxyPXRoaXMscT0iTm9TdWNoTWV0aG9kRXJyb3I6IG1l
-dGhvZCBub3QgZm91bmQ6ICciLHA9ci5iCmlmKHA9PW51bGwpcmV0dXJuIk5vU3VjaE1ldGhvZEVycm9y
-OiAiK0guRWooci5hKQpzPXIuYwppZihzPT1udWxsKXJldHVybiBxK3ArIicgKCIrSC5FaihyLmEpKyIp
-IgpyZXR1cm4gcStwKyInIG9uICciK3MrIicgKCIrSC5FaihyLmEpKyIpIn19CkgudlYucHJvdG90eXBl
-PXsKdzpmdW5jdGlvbihhKXt2YXIgcz10aGlzLmEKcmV0dXJuIHMubGVuZ3RoPT09MD8iRXJyb3IiOiJF
-cnJvcjogIitzfX0KSC50ZS5wcm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEpe3JldHVybiJUaHJvdyBvZiBu
-dWxsICgnIisodGhpcy5hPT09bnVsbD8ibnVsbCI6InVuZGVmaW5lZCIpKyInIGZyb20gSmF2YVNjcmlw
-dCkifSwKJGlSejoxfQpILmJxLnByb3RvdHlwZT17fQpILlhPLnByb3RvdHlwZT17Cnc6ZnVuY3Rpb24o
-YSl7dmFyIHMscj10aGlzLmIKaWYociE9bnVsbClyZXR1cm4gcgpyPXRoaXMuYQpzPXIhPT1udWxsJiZ0
-eXBlb2Ygcj09PSJvYmplY3QiP3Iuc3RhY2s6bnVsbApyZXR1cm4gdGhpcy5iPXM9PW51bGw/IiI6c30s
-CiRpR3o6MX0KSC5UcC5wcm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEpe3ZhciBzPXRoaXMuY29uc3RydWN0
-b3Iscj1zPT1udWxsP251bGw6cy5uYW1lCnJldHVybiJDbG9zdXJlICciK0gucChyPT1udWxsPyJ1bmtu
-b3duIjpyKSsiJyJ9LAokaUVIOjEsCmdLdTpmdW5jdGlvbigpe3JldHVybiB0aGlzfSwKJEM6IiQxIiwK
-JFI6MSwKJEQ6bnVsbH0KSC5sYy5wcm90b3R5cGU9e30KSC56eC5wcm90b3R5cGU9ewp3OmZ1bmN0aW9u
-KGEpe3ZhciBzPXRoaXMuJHN0YXRpY19uYW1lCmlmKHM9PW51bGwpcmV0dXJuIkNsb3N1cmUgb2YgdW5r
-bm93biBzdGF0aWMgbWV0aG9kIgpyZXR1cm4iQ2xvc3VyZSAnIitILnAocykrIicifX0KSC5yVC5wcm90
-b3R5cGU9ewpETjpmdW5jdGlvbihhLGIpe3ZhciBzPXRoaXMKaWYoYj09bnVsbClyZXR1cm4hMQppZihz
-PT09YilyZXR1cm4hMAppZighKGIgaW5zdGFuY2VvZiBILnJUKSlyZXR1cm4hMQpyZXR1cm4gcy5hPT09
-Yi5hJiZzLiRfdGFyZ2V0PT09Yi4kX3RhcmdldCYmcy5iPT09Yi5ifSwKZ2lPOmZ1bmN0aW9uKGEpe3Zh
-ciBzLHI9dGhpcy5iCmlmKHI9PW51bGwpcz1ILmVRKHRoaXMuYSkKZWxzZSBzPXR5cGVvZiByIT09Im9i
-amVjdCI/Si5oZihyKTpILmVRKHIpCnI9SC5lUSh0aGlzLiRfdGFyZ2V0KQppZih0eXBlb2YgcyE9PSJu
-dW1iZXIiKXJldHVybiBzLlkoKQpyZXR1cm4oc15yKT4+PjB9LAp3OmZ1bmN0aW9uKGEpe3ZhciBzPXRo
-aXMuYgppZihzPT1udWxsKXM9dGhpcy5hCnJldHVybiJDbG9zdXJlICciK0guRWoodGhpcy4kX25hbWUp
-KyInIG9mICIrKCJJbnN0YW5jZSBvZiAnIitILkVqKEgubGgocykpKyInIil9fQpILkVxLnByb3RvdHlw
+dHVybiB0aGlzLmF9fQpILmMucHJvdG90eXBlPXsKdzpmdW5jdGlvbihhKXt2YXIgcz0iTGF0ZUluaXRp
+YWxpemF0aW9uRXJyb3I6ICIrdGhpcy5hCnJldHVybiBzfX0KSC5xai5wcm90b3R5cGU9ewpnQTpmdW5j
+dGlvbihhKXtyZXR1cm4gdGhpcy5hLmxlbmd0aH0sCnE6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gQy54Qi5P
+KHRoaXMuYSxILklaKGIpKX19CkguYlEucHJvdG90eXBlPXt9CkguYUwucHJvdG90eXBlPXsKZ206ZnVu
+Y3Rpb24oYSl7dmFyIHM9dGhpcwpyZXR1cm4gbmV3IEguYTcocyxzLmdBKHMpLEguTGgocykuQygiYTc8
+YUwuRT4iKSl9LApnbDA6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuZ0EodGhpcyk9PT0wfSwKazpmdW5j
+dGlvbihhLGIpe3ZhciBzLHIscSxwPXRoaXMsbz1wLmdBKHApCmlmKGIubGVuZ3RoIT09MCl7aWYobz09
+PTApcmV0dXJuIiIKcz1ILkVqKHAuRSgwLDApKQppZihvIT09cC5nQShwKSl0aHJvdyBILmIoUC5hNChw
+KSkKZm9yKHI9cyxxPTE7cTxvOysrcSl7cj1yK2IrSC5FaihwLkUoMCxxKSkKaWYobyE9PXAuZ0EocCkp
+dGhyb3cgSC5iKFAuYTQocCkpfXJldHVybiByLmNoYXJDb2RlQXQoMCk9PTA/cjpyfWVsc2V7Zm9yKHE9
+MCxyPSIiO3E8bzsrK3Epe3IrPUguRWoocC5FKDAscSkpCmlmKG8hPT1wLmdBKHApKXRocm93IEguYihQ
+LmE0KHApKX1yZXR1cm4gci5jaGFyQ29kZUF0KDApPT0wP3I6cn19LApldjpmdW5jdGlvbihhLGIpe3Jl
+dHVybiB0aGlzLkdHKDAsSC5MaCh0aGlzKS5DKCJhMihhTC5FKSIpLmEoYikpfSwKRTI6ZnVuY3Rpb24o
+YSxiLGMpe3ZhciBzPUguTGgodGhpcykKcmV0dXJuIG5ldyBILmxKKHRoaXMscy5LcShjKS5DKCIxKGFM
+LkUpIikuYShiKSxzLkMoIkA8YUwuRT4iKS5LcShjKS5DKCJsSjwxLDI+IikpfSwKZVI6ZnVuY3Rpb24o
+YSxiKXtyZXR1cm4gSC5xQyh0aGlzLGIsbnVsbCxILkxoKHRoaXMpLkMoImFMLkUiKSl9LAp0dDpmdW5j
+dGlvbihhLGIpe3JldHVybiBQLlkxKHRoaXMsITAsSC5MaCh0aGlzKS5DKCJhTC5FIikpfSwKYnI6ZnVu
+Y3Rpb24oYSl7cmV0dXJuIHRoaXMudHQoYSwhMCl9fQpILm5ILnByb3RvdHlwZT17CkhkOmZ1bmN0aW9u
+KGEsYixjLGQpe3ZhciBzLHI9dGhpcy5iClAuazEociwic3RhcnQiKQpzPXRoaXMuYwppZihzIT1udWxs
+KXtQLmsxKHMsImVuZCIpCmlmKHI+cyl0aHJvdyBILmIoUC5URShyLDAscywic3RhcnQiLG51bGwpKX19
+LApnVUQ6ZnVuY3Rpb24oKXt2YXIgcz1KLkhtKHRoaXMuYSkscj10aGlzLmMKaWYocj09bnVsbHx8cj5z
+KXJldHVybiBzCnJldHVybiByfSwKZ0FzOmZ1bmN0aW9uKCl7dmFyIHM9Si5IbSh0aGlzLmEpLHI9dGhp
+cy5iCmlmKHI+cylyZXR1cm4gcwpyZXR1cm4gcn0sCmdBOmZ1bmN0aW9uKGEpe3ZhciBzLHI9Si5IbSh0
+aGlzLmEpLHE9dGhpcy5iCmlmKHE+PXIpcmV0dXJuIDAKcz10aGlzLmMKaWYocz09bnVsbHx8cz49cily
+ZXR1cm4gci1xCmlmKHR5cGVvZiBzIT09Im51bWJlciIpcmV0dXJuIHMuSE4oKQpyZXR1cm4gcy1xfSwK
+RTpmdW5jdGlvbihhLGIpe3ZhciBzPXRoaXMscj1zLmdBcygpK2IKaWYoYjwwfHxyPj1zLmdVRCgpKXRo
+cm93IEguYihQLkNmKGIscywiaW5kZXgiLG51bGwsbnVsbCkpCnJldHVybiBKLkdBKHMuYSxyKX0sCmVS
+OmZ1bmN0aW9uKGEsYil7dmFyIHMscixxPXRoaXMKUC5rMShiLCJjb3VudCIpCnM9cS5iK2IKcj1xLmMK
+aWYociE9bnVsbCYmcz49cilyZXR1cm4gbmV3IEguTUIocS4kdGkuQygiTUI8MT4iKSkKcmV0dXJuIEgu
+cUMocS5hLHMscixxLiR0aS5jKX0sCnR0OmZ1bmN0aW9uKGEsYil7dmFyIHMscixxLHA9dGhpcyxvPXAu
+YixuPXAuYSxtPUouVTYobiksbD1tLmdBKG4pLGs9cC5jCmlmKGshPW51bGwmJms8bClsPWsKcz1sLW8K
+aWYoczw9MCl7bj1KLlFpKDAscC4kdGkuYykKcmV0dXJuIG59cj1QLk84KHMsbS5FKG4sbyksITEscC4k
+dGkuYykKZm9yKHE9MTtxPHM7KytxKXtDLk5tLlk1KHIscSxtLkUobixvK3EpKQppZihtLmdBKG4pPGwp
+dGhyb3cgSC5iKFAuYTQocCkpfXJldHVybiByfX0KSC5hNy5wcm90b3R5cGU9ewpnbDpmdW5jdGlvbigp
+e3JldHVybiB0aGlzLiR0aS5jLmEodGhpcy5kKX0sCkY6ZnVuY3Rpb24oKXt2YXIgcyxyPXRoaXMscT1y
+LmEscD1KLlU2KHEpLG89cC5nQShxKQppZihyLmIhPT1vKXRocm93IEguYihQLmE0KHEpKQpzPXIuYwpp
+ZihzPj1vKXtyLnNJKG51bGwpCnJldHVybiExfXIuc0kocC5FKHEscykpOysrci5jCnJldHVybiEwfSwK
+c0k6ZnVuY3Rpb24oYSl7dGhpcy5kPXRoaXMuJHRpLkMoIjE/IikuYShhKX0sCiRpQW46MX0KSC5pMS5w
+cm90b3R5cGU9ewpnbTpmdW5jdGlvbihhKXt2YXIgcz1ILkxoKHRoaXMpCnJldHVybiBuZXcgSC5NSChK
+LklUKHRoaXMuYSksdGhpcy5iLHMuQygiQDwxPiIpLktxKHMuUVsxXSkuQygiTUg8MSwyPiIpKX0sCmdB
+OmZ1bmN0aW9uKGEpe3JldHVybiBKLkhtKHRoaXMuYSl9LApnbDA6ZnVuY3Rpb24oYSl7cmV0dXJuIEou
+dVUodGhpcy5hKX0sCkU6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gdGhpcy5iLiQxKEouR0EodGhpcy5hLGIp
+KX19CkgueHkucHJvdG90eXBlPXskaWJROjF9CkguTUgucHJvdG90eXBlPXsKRjpmdW5jdGlvbigpe3Zh
+ciBzPXRoaXMscj1zLmIKaWYoci5GKCkpe3Muc0kocy5jLiQxKHIuZ2woKSkpCnJldHVybiEwfXMuc0ko
+bnVsbCkKcmV0dXJuITF9LApnbDpmdW5jdGlvbigpe3JldHVybiB0aGlzLiR0aS5RWzFdLmEodGhpcy5h
+KX0sCnNJOmZ1bmN0aW9uKGEpe3RoaXMuYT10aGlzLiR0aS5DKCIyPyIpLmEoYSl9fQpILmxKLnByb3Rv
+dHlwZT17CmdBOmZ1bmN0aW9uKGEpe3JldHVybiBKLkhtKHRoaXMuYSl9LApFOmZ1bmN0aW9uKGEsYil7
+cmV0dXJuIHRoaXMuYi4kMShKLkdBKHRoaXMuYSxiKSl9fQpILlU1LnByb3RvdHlwZT17CmdtOmZ1bmN0
+aW9uKGEpe3JldHVybiBuZXcgSC5TTyhKLklUKHRoaXMuYSksdGhpcy5iLHRoaXMuJHRpLkMoIlNPPDE+
+IikpfX0KSC5TTy5wcm90b3R5cGU9ewpGOmZ1bmN0aW9uKCl7dmFyIHMscgpmb3Iocz10aGlzLmEscj10
+aGlzLmI7cy5GKCk7KWlmKEgub1Qoci4kMShzLmdsKCkpKSlyZXR1cm4hMApyZXR1cm4hMX0sCmdsOmZ1
+bmN0aW9uKCl7cmV0dXJuIHRoaXMuYS5nbCgpfX0KSC5BTS5wcm90b3R5cGU9ewplUjpmdW5jdGlvbihh
+LGIpe1AuazEoYiwiY291bnQiKQpyZXR1cm4gbmV3IEguQU0odGhpcy5hLHRoaXMuYitiLEguTGgodGhp
+cykuQygiQU08MT4iKSl9LApnbTpmdW5jdGlvbihhKXtyZXR1cm4gbmV3IEguVTEoSi5JVCh0aGlzLmEp
+LHRoaXMuYixILkxoKHRoaXMpLkMoIlUxPDE+IikpfX0KSC5kNS5wcm90b3R5cGU9ewpnQTpmdW5jdGlv
+bihhKXt2YXIgcz1KLkhtKHRoaXMuYSktdGhpcy5iCmlmKHM+PTApcmV0dXJuIHMKcmV0dXJuIDB9LApl
+UjpmdW5jdGlvbihhLGIpe1AuazEoYiwiY291bnQiKQpyZXR1cm4gbmV3IEguZDUodGhpcy5hLHRoaXMu
+YitiLHRoaXMuJHRpKX0sCiRpYlE6MX0KSC5VMS5wcm90b3R5cGU9ewpGOmZ1bmN0aW9uKCl7dmFyIHMs
+cgpmb3Iocz10aGlzLmEscj0wO3I8dGhpcy5iOysrcilzLkYoKQp0aGlzLmI9MApyZXR1cm4gcy5GKCl9
+LApnbDpmdW5jdGlvbigpe3JldHVybiB0aGlzLmEuZ2woKX19CkguTUIucHJvdG90eXBlPXsKZ206ZnVu
+Y3Rpb24oYSl7cmV0dXJuIEMuR3d9LApnbDA6ZnVuY3Rpb24oYSl7cmV0dXJuITB9LApnQTpmdW5jdGlv
+bihhKXtyZXR1cm4gMH0sCkU6ZnVuY3Rpb24oYSxiKXt0aHJvdyBILmIoUC5URShiLDAsMCwiaW5kZXgi
+LG51bGwpKX0sCmVSOmZ1bmN0aW9uKGEsYil7UC5rMShiLCJjb3VudCIpCnJldHVybiB0aGlzfX0KSC5G
+dS5wcm90b3R5cGU9ewpGOmZ1bmN0aW9uKCl7cmV0dXJuITF9LApnbDpmdW5jdGlvbigpe3Rocm93IEgu
+YihILldwKCkpfSwKJGlBbjoxfQpILnU2LnByb3RvdHlwZT17CmdtOmZ1bmN0aW9uKGEpe3JldHVybiBu
+ZXcgSC5KQihKLklUKHRoaXMuYSksdGhpcy4kdGkuQygiSkI8MT4iKSl9fQpILkpCLnByb3RvdHlwZT17
+CkY6ZnVuY3Rpb24oKXt2YXIgcyxyCmZvcihzPXRoaXMuYSxyPXRoaXMuJHRpLmM7cy5GKCk7KWlmKHIu
+YihzLmdsKCkpKXJldHVybiEwCnJldHVybiExfSwKZ2w6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy4kdGku
+Yy5hKHRoaXMuYS5nbCgpKX0sCiRpQW46MX0KSC5TVS5wcm90b3R5cGU9e30KSC5SZS5wcm90b3R5cGU9
+ewpZNTpmdW5jdGlvbihhLGIsYyl7SC5MaCh0aGlzKS5DKCJSZS5FIikuYShjKQp0aHJvdyBILmIoUC5M
+NCgiQ2Fubm90IG1vZGlmeSBhbiB1bm1vZGlmaWFibGUgbGlzdCIpKX19CkgudzIucHJvdG90eXBlPXt9
+Ckgud3YucHJvdG90eXBlPXsKZ2lPOmZ1bmN0aW9uKGEpe3ZhciBzPXRoaXMuX2hhc2hDb2RlCmlmKHMh
+PW51bGwpcmV0dXJuIHMKcz02NjQ1OTcqSi5oZih0aGlzLmEpJjUzNjg3MDkxMQp0aGlzLl9oYXNoQ29k
+ZT1zCnJldHVybiBzfSwKdzpmdW5jdGlvbihhKXtyZXR1cm4nU3ltYm9sKCInK0guRWoodGhpcy5hKSsn
+IiknfSwKRE46ZnVuY3Rpb24oYSxiKXtpZihiPT1udWxsKXJldHVybiExCnJldHVybiBiIGluc3RhbmNl
+b2YgSC53diYmdGhpcy5hPT1iLmF9LAokaUdEOjF9CkguUUMucHJvdG90eXBlPXt9CkguUEQucHJvdG90
+eXBlPXt9CkguV1UucHJvdG90eXBlPXsKZ2wwOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmdBKHRoaXMp
+PT09MH0sCnc6ZnVuY3Rpb24oYSl7cmV0dXJuIFAubk8odGhpcyl9LApZNTpmdW5jdGlvbihhLGIsYyl7
+dmFyIHM9SC5MaCh0aGlzKQpzLmMuYShiKQpzLlFbMV0uYShjKQpILmRjKCl9LApnUHU6ZnVuY3Rpb24o
+YSl7cmV0dXJuIHRoaXMucTQoMCxILkxoKHRoaXMpLkMoIk4zPDEsMj4iKSl9LApxNDpmdW5jdGlvbihh
+LGIpe3ZhciBzPXRoaXMKcmV0dXJuIFAubDAoZnVuY3Rpb24oKXt2YXIgcj1hCnZhciBxPTAscD0xLG8s
+bixtLGwsawpyZXR1cm4gZnVuY3Rpb24gJGFzeW5jJGdQdShjLGQpe2lmKGM9PT0xKXtvPWQKcT1wfXdo
+aWxlKHRydWUpc3dpdGNoKHEpe2Nhc2UgMDpuPXMuZ3ZjKCksbj1uLmdtKG4pLG09SC5MaChzKSxtPW0u
+QygiQDwxPiIpLktxKG0uUVsxXSkuQygiTjM8MSwyPiIpCmNhc2UgMjppZighbi5GKCkpe3E9MwpicmVh
+a31sPW4uZ2woKQprPXMucSgwLGwpCmsudG9TdHJpbmcKcT00CnJldHVybiBuZXcgUC5OMyhsLGssbSkK
+Y2FzZSA0OnE9MgpicmVhawpjYXNlIDM6cmV0dXJuIFAuVGgoKQpjYXNlIDE6cmV0dXJuIFAuWW0obyl9
+fX0sYil9LAokaVowOjF9CkguTFAucHJvdG90eXBlPXsKZ0E6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMu
+YX0sCng0OmZ1bmN0aW9uKGEpe2lmKHR5cGVvZiBhIT0ic3RyaW5nIilyZXR1cm4hMQppZigiX19wcm90
+b19fIj09PWEpcmV0dXJuITEKcmV0dXJuIHRoaXMuYi5oYXNPd25Qcm9wZXJ0eShhKX0sCnE6ZnVuY3Rp
+b24oYSxiKXtpZighdGhpcy54NChiKSlyZXR1cm4gbnVsbApyZXR1cm4gdGhpcy5xUChiKX0sCnFQOmZ1
+bmN0aW9uKGEpe3JldHVybiB0aGlzLmJbSC5uKGEpXX0sCks6ZnVuY3Rpb24oYSxiKXt2YXIgcyxyLHEs
+cCxvPUguTGgodGhpcykKby5DKCJ+KDEsMikiKS5hKGIpCnM9dGhpcy5jCmZvcihyPXMubGVuZ3RoLG89
+by5RWzFdLHE9MDtxPHI7KytxKXtwPXNbcV0KYi4kMihwLG8uYSh0aGlzLnFQKHApKSl9fSwKZ3ZjOmZ1
+bmN0aW9uKCl7cmV0dXJuIG5ldyBILlhSKHRoaXMsSC5MaCh0aGlzKS5DKCJYUjwxPiIpKX19CkguWFIu
+cHJvdG90eXBlPXsKZ206ZnVuY3Rpb24oYSl7dmFyIHM9dGhpcy5hLmMKcmV0dXJuIG5ldyBKLm0xKHMs
+cy5sZW5ndGgsSC50NihzKS5DKCJtMTwxPiIpKX0sCmdBOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmEu
+Yy5sZW5ndGh9fQpILkxJLnByb3RvdHlwZT17CmdXYTpmdW5jdGlvbigpe3ZhciBzPXRoaXMuYQpyZXR1
+cm4gc30sCmduZDpmdW5jdGlvbigpe3ZhciBzLHIscSxwLG89dGhpcwppZihvLmM9PT0xKXJldHVybiBD
+LmhVCnM9by5kCnI9cy5sZW5ndGgtby5lLmxlbmd0aC1vLmYKaWYocj09PTApcmV0dXJuIEMuaFUKcT1b
+XQpmb3IocD0wO3A8cjsrK3Ape2lmKHA+PXMubGVuZ3RoKXJldHVybiBILk9IKHMscCkKcS5wdXNoKHNb
+cF0pfXJldHVybiBKLnpDKHEpfSwKZ1ZtOmZ1bmN0aW9uKCl7dmFyIHMscixxLHAsbyxuLG0sbCxrPXRo
+aXMKaWYoay5jIT09MClyZXR1cm4gQy5XTwpzPWsuZQpyPXMubGVuZ3RoCnE9ay5kCnA9cS5sZW5ndGgt
+ci1rLmYKaWYocj09PTApcmV0dXJuIEMuV08Kbz1uZXcgSC5ONSh0LmVvKQpmb3Iobj0wO248cjsrK24p
+e2lmKG4+PXMubGVuZ3RoKXJldHVybiBILk9IKHMsbikKbT1zW25dCmw9cCtuCmlmKGw8MHx8bD49cS5s
+ZW5ndGgpcmV0dXJuIEguT0gocSxsKQpvLlk1KDAsbmV3IEgud3YobSkscVtsXSl9cmV0dXJuIG5ldyBI
+LlBEKG8sdC5nRil9LAokaXZROjF9CkguQ2oucHJvdG90eXBlPXsKJDI6ZnVuY3Rpb24oYSxiKXt2YXIg
+cwpILm4oYSkKcz10aGlzLmEKcy5iPXMuYisiJCIrYQpDLk5tLmkodGhpcy5iLGEpCkMuTm0uaSh0aGlz
+LmMsYik7KytzLmF9LAokUzoxMX0KSC5mOS5wcm90b3R5cGU9ewpxUzpmdW5jdGlvbihhKXt2YXIgcyxy
+LHE9dGhpcyxwPW5ldyBSZWdFeHAocS5hKS5leGVjKGEpCmlmKHA9PW51bGwpcmV0dXJuIG51bGwKcz1P
+YmplY3QuY3JlYXRlKG51bGwpCnI9cS5iCmlmKHIhPT0tMSlzLmFyZ3VtZW50cz1wW3IrMV0Kcj1xLmMK
+aWYociE9PS0xKXMuYXJndW1lbnRzRXhwcj1wW3IrMV0Kcj1xLmQKaWYociE9PS0xKXMuZXhwcj1wW3Ir
+MV0Kcj1xLmUKaWYociE9PS0xKXMubWV0aG9kPXBbcisxXQpyPXEuZgppZihyIT09LTEpcy5yZWNlaXZl
+cj1wW3IrMV0KcmV0dXJuIHN9fQpILlcwLnByb3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7dmFyIHM9dGhp
+cy5iCmlmKHM9PW51bGwpcmV0dXJuIk5vU3VjaE1ldGhvZEVycm9yOiAiK3RoaXMuYQpyZXR1cm4iTm9T
+dWNoTWV0aG9kRXJyb3I6IG1ldGhvZCBub3QgZm91bmQ6ICciK3MrIicgb24gbnVsbCJ9fQpILmF6LnBy
+b3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7dmFyIHMscj10aGlzLHE9Ik5vU3VjaE1ldGhvZEVycm9yOiBt
+ZXRob2Qgbm90IGZvdW5kOiAnIixwPXIuYgppZihwPT1udWxsKXJldHVybiJOb1N1Y2hNZXRob2RFcnJv
+cjogIityLmEKcz1yLmMKaWYocz09bnVsbClyZXR1cm4gcStwKyInICgiK3IuYSsiKSIKcmV0dXJuIHEr
+cCsiJyBvbiAnIitzKyInICgiK3IuYSsiKSJ9fQpILnZWLnByb3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7
+dmFyIHM9dGhpcy5hCnJldHVybiBzLmxlbmd0aD09PTA/IkVycm9yIjoiRXJyb3I6ICIrc319CkgudGUu
+cHJvdG90eXBlPXsKdzpmdW5jdGlvbihhKXtyZXR1cm4iVGhyb3cgb2YgbnVsbCAoJyIrKHRoaXMuYT09
+PW51bGw/Im51bGwiOiJ1bmRlZmluZWQiKSsiJyBmcm9tIEphdmFTY3JpcHQpIn0sCiRpUno6MX0KSC5i
+cS5wcm90b3R5cGU9e30KSC5YTy5wcm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEpe3ZhciBzLHI9dGhpcy5i
+CmlmKHIhPW51bGwpcmV0dXJuIHIKcj10aGlzLmEKcz1yIT09bnVsbCYmdHlwZW9mIHI9PT0ib2JqZWN0
+Ij9yLnN0YWNrOm51bGwKcmV0dXJuIHRoaXMuYj1zPT1udWxsPyIiOnN9LAokaUd6OjF9CkguVHAucHJv
+dG90eXBlPXsKdzpmdW5jdGlvbihhKXt2YXIgcz10aGlzLmNvbnN0cnVjdG9yLHI9cz09bnVsbD9udWxs
+OnMubmFtZQpyZXR1cm4iQ2xvc3VyZSAnIitILk5RKHI9PW51bGw/InVua25vd24iOnIpKyInIn0sCiRp
+RUg6MSwKZ0t1OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXN9LAokQzoiJDEiLAokUjoxLAokRDpudWxsfQpI
+LmxjLnByb3RvdHlwZT17fQpILnp4LnByb3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7dmFyIHM9dGhpcy4k
+c3RhdGljX25hbWUKaWYocz09bnVsbClyZXR1cm4iQ2xvc3VyZSBvZiB1bmtub3duIHN0YXRpYyBtZXRo
+b2QiCnJldHVybiJDbG9zdXJlICciK0guTlEocykrIicifX0KSC5yVC5wcm90b3R5cGU9ewpETjpmdW5j
+dGlvbihhLGIpe3ZhciBzPXRoaXMKaWYoYj09bnVsbClyZXR1cm4hMQppZihzPT09YilyZXR1cm4hMApp
+ZighKGIgaW5zdGFuY2VvZiBILnJUKSlyZXR1cm4hMQpyZXR1cm4gcy5hPT09Yi5hJiZzLiRfdGFyZ2V0
+PT09Yi4kX3RhcmdldCYmcy5iPT09Yi5ifSwKZ2lPOmZ1bmN0aW9uKGEpe3ZhciBzLHI9dGhpcy5iCmlm
+KHI9PW51bGwpcz1ILmVRKHRoaXMuYSkKZWxzZSBzPXR5cGVvZiByIT09Im9iamVjdCI/Si5oZihyKTpI
+LmVRKHIpCnJldHVybihzXkguZVEodGhpcy4kX3RhcmdldCkpPj4+MH0sCnc6ZnVuY3Rpb24oYSl7dmFy
+IHM9dGhpcy5iCmlmKHM9PW51bGwpcz10aGlzLmEKcmV0dXJuIkNsb3N1cmUgJyIrdGhpcy4kX25hbWUr
+Iicgb2YgIisoIkluc3RhbmNlIG9mICciK0gubGgodC5LLmEocykpKyInIil9fQpILkVxLnByb3RvdHlw
ZT17Cnc6ZnVuY3Rpb24oYSl7cmV0dXJuIlJ1bnRpbWVFcnJvcjogIit0aGlzLmF9fQpILmtZLnByb3Rv
dHlwZT17Cnc6ZnVuY3Rpb24oYSl7cmV0dXJuIkFzc2VydGlvbiBmYWlsZWQ6ICIrUC5obCh0aGlzLmEp
fX0KSC5rci5wcm90b3R5cGU9e30KSC5ONS5wcm90b3R5cGU9ewpnQTpmdW5jdGlvbihhKXtyZXR1cm4g
dGhpcy5hfSwKZ2wwOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmE9PT0wfSwKZ3ZjOmZ1bmN0aW9uKCl7
cmV0dXJuIG5ldyBILmk1KHRoaXMsSC5MaCh0aGlzKS5DKCJpNTwxPiIpKX0sCng0OmZ1bmN0aW9uKGEp
-e3ZhciBzLHIKaWYodHlwZW9mIGE9PSJzdHJpbmciKXtzPXRoaXMuYgppZihzPT1udWxsKXJldHVybiEx
-CnJldHVybiB0aGlzLlh1KHMsYSl9ZWxzZXtyPXRoaXMuQ1goYSkKcmV0dXJuIHJ9fSwKQ1g6ZnVuY3Rp
-b24oYSl7dmFyIHM9dGhpcy5kCmlmKHM9PW51bGwpcmV0dXJuITEKcmV0dXJuIHRoaXMuRmgodGhpcy5C
-dChzLEouaGYoYSkmMHgzZmZmZmZmKSxhKT49MH0sCnE6ZnVuY3Rpb24oYSxiKXt2YXIgcyxyLHEscCxv
-PXRoaXMsbj1udWxsCmlmKHR5cGVvZiBiPT0ic3RyaW5nIil7cz1vLmIKaWYocz09bnVsbClyZXR1cm4g
-bgpyPW8uajIocyxiKQpxPXI9PW51bGw/bjpyLmIKcmV0dXJuIHF9ZWxzZSBpZih0eXBlb2YgYj09Im51
-bWJlciImJihiJjB4M2ZmZmZmZik9PT1iKXtwPW8uYwppZihwPT1udWxsKXJldHVybiBuCnI9by5qMihw
-LGIpCnE9cj09bnVsbD9uOnIuYgpyZXR1cm4gcX1lbHNlIHJldHVybiBvLmFhKGIpfSwKYWE6ZnVuY3Rp
-b24oYSl7dmFyIHMscixxPXRoaXMuZAppZihxPT1udWxsKXJldHVybiBudWxsCnM9dGhpcy5CdChxLEou
-aGYoYSkmMHgzZmZmZmZmKQpyPXRoaXMuRmgocyxhKQppZihyPDApcmV0dXJuIG51bGwKcmV0dXJuIHNb
-cl0uYn0sClk1OmZ1bmN0aW9uKGEsYixjKXt2YXIgcyxyLHEscCxvLG4sbT10aGlzLGw9SC5MaChtKQps
-LmMuYShiKQpsLlFbMV0uYShjKQppZih0eXBlb2YgYj09InN0cmluZyIpe3M9bS5iCm0uRUgocz09bnVs
-bD9tLmI9bS56SygpOnMsYixjKX1lbHNlIGlmKHR5cGVvZiBiPT0ibnVtYmVyIiYmKGImMHgzZmZmZmZm
-KT09PWIpe3I9bS5jCm0uRUgocj09bnVsbD9tLmM9bS56SygpOnIsYixjKX1lbHNle3E9bS5kCmlmKHE9
-PW51bGwpcT1tLmQ9bS56SygpCnA9Si5oZihiKSYweDNmZmZmZmYKbz1tLkJ0KHEscCkKaWYobz09bnVs
-bCltLkVJKHEscCxbbS5IbihiLGMpXSkKZWxzZXtuPW0uRmgobyxiKQppZihuPj0wKW9bbl0uYj1jCmVs
-c2Ugby5wdXNoKG0uSG4oYixjKSl9fX0sCks6ZnVuY3Rpb24oYSxiKXt2YXIgcyxyLHE9dGhpcwpILkxo
-KHEpLkMoIn4oMSwyKSIpLmEoYikKcz1xLmUKcj1xLnIKZm9yKDtzIT1udWxsOyl7Yi4kMihzLmEscy5i
-KQppZihyIT09cS5yKXRocm93IEguYihQLmE0KHEpKQpzPXMuY319LApFSDpmdW5jdGlvbihhLGIsYyl7
-dmFyIHMscj10aGlzLHE9SC5MaChyKQpxLmMuYShiKQpxLlFbMV0uYShjKQpzPXIuajIoYSxiKQppZihz
-PT1udWxsKXIuRUkoYSxiLHIuSG4oYixjKSkKZWxzZSBzLmI9Y30sCmtzOmZ1bmN0aW9uKCl7dGhpcy5y
-PXRoaXMucisxJjY3MTA4ODYzfSwKSG46ZnVuY3Rpb24oYSxiKXt2YXIgcz10aGlzLHI9SC5MaChzKSxx
-PW5ldyBILnZoKHIuYy5hKGEpLHIuUVsxXS5hKGIpKQppZihzLmU9PW51bGwpcy5lPXMuZj1xCmVsc2V7
-cj1zLmYKci50b1N0cmluZwpxLmQ9cgpzLmY9ci5jPXF9KytzLmEKcy5rcygpCnJldHVybiBxfSwKRmg6
-ZnVuY3Rpb24oYSxiKXt2YXIgcyxyCmlmKGE9PW51bGwpcmV0dXJuLTEKcz1hLmxlbmd0aApmb3Iocj0w
-O3I8czsrK3IpaWYoSi5STShhW3JdLmEsYikpcmV0dXJuIHIKcmV0dXJuLTF9LAp3OmZ1bmN0aW9uKGEp
-e3JldHVybiBQLm5PKHRoaXMpfSwKajI6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gYVtiXX0sCkJ0OmZ1bmN0
-aW9uKGEsYil7cmV0dXJuIGFbYl19LApFSTpmdW5jdGlvbihhLGIsYyl7YVtiXT1jfSwKcm46ZnVuY3Rp
-b24oYSxiKXtkZWxldGUgYVtiXX0sClh1OmZ1bmN0aW9uKGEsYil7cmV0dXJuIHRoaXMuajIoYSxiKSE9
-bnVsbH0sCnpLOmZ1bmN0aW9uKCl7dmFyIHM9Ijxub24taWRlbnRpZmllci1rZXk+IixyPU9iamVjdC5j
-cmVhdGUobnVsbCkKdGhpcy5FSShyLHMscikKdGhpcy5ybihyLHMpCnJldHVybiByfSwKJGlGbzoxfQpI
-LnZoLnByb3RvdHlwZT17fQpILmk1LnByb3RvdHlwZT17CmdBOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlz
-LmEuYX0sCmdsMDpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5hLmE9PT0wfSwKZ206ZnVuY3Rpb24oYSl7
-dmFyIHM9dGhpcy5hLHI9bmV3IEguTjYocyxzLnIsdGhpcy4kdGkuQygiTjY8MT4iKSkKci5jPXMuZQpy
-ZXR1cm4gcn0sCnRnOmZ1bmN0aW9uKGEsYil7cmV0dXJuIHRoaXMuYS54NChiKX19CkguTjYucHJvdG90
-eXBlPXsKZ2w6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5kfSwKRjpmdW5jdGlvbigpe3ZhciBzLHI9dGhp
-cyxxPXIuYQppZihyLmIhPT1xLnIpdGhyb3cgSC5iKFAuYTQocSkpCnM9ci5jCmlmKHM9PW51bGwpe3Iu
-c3FZKG51bGwpCnJldHVybiExfWVsc2V7ci5zcVkocy5hKQpyLmM9cy5jCnJldHVybiEwfX0sCnNxWTpm
-dW5jdGlvbihhKXt0aGlzLmQ9dGhpcy4kdGkuQygiMT8iKS5hKGEpfSwKJGlBbjoxfQpILmRDLnByb3Rv
-dHlwZT17CiQxOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmEoYSl9LAokUzo0fQpILndOLnByb3RvdHlw
-ZT17CiQyOmZ1bmN0aW9uKGEsYil7cmV0dXJuIHRoaXMuYShhLGIpfSwKJFM6MzR9CkguVlgucHJvdG90
-eXBlPXsKJDE6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuYShILmgoYSkpfSwKJFM6MzJ9CkguVlIucHJv
-dG90eXBlPXsKdzpmdW5jdGlvbihhKXtyZXR1cm4iUmVnRXhwLyIrdGhpcy5hKyIvIit0aGlzLmIuZmxh
-Z3N9LApnSGM6ZnVuY3Rpb24oKXt2YXIgcz10aGlzLHI9cy5jCmlmKHIhPW51bGwpcmV0dXJuIHIKcj1z
-LmIKcmV0dXJuIHMuYz1ILnY0KHMuYSxyLm11bHRpbGluZSwhci5pZ25vcmVDYXNlLHIudW5pY29kZSxy
-LmRvdEFsbCwhMCl9LApkZDpmdW5jdGlvbihhLGIpe3JldHVybiBuZXcgSC5LVyh0aGlzLGIsMCl9LApV
-WjpmdW5jdGlvbihhLGIpe3ZhciBzLHI9dGhpcy5nSGMoKQpyLmxhc3RJbmRleD1iCnM9ci5leGVjKGEp
-CmlmKHM9PW51bGwpcmV0dXJuIG51bGwKcmV0dXJuIG5ldyBILkVLKHMpfSwKJGl2WDoxLAokaXdMOjF9
-CkguRUsucHJvdG90eXBlPXsKcTpmdW5jdGlvbihhLGIpe3ZhciBzCkgudVAoYikKcz10aGlzLmIKaWYo
-Yj49cy5sZW5ndGgpcmV0dXJuIEguT0gocyxiKQpyZXR1cm4gc1tiXX0sCiRpT2Q6MSwKJGlpYjoxfQpI
-LktXLnByb3RvdHlwZT17CmdtOmZ1bmN0aW9uKGEpe3JldHVybiBuZXcgSC5QYih0aGlzLmEsdGhpcy5i
-LHRoaXMuYyl9fQpILlBiLnByb3RvdHlwZT17CmdsOmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuZH0sCkY6
-ZnVuY3Rpb24oKXt2YXIgcyxyLHEscCxvLG4sbT10aGlzLGw9bS5iCmlmKGw9PW51bGwpcmV0dXJuITEK
-cz1tLmMKcj1sLmxlbmd0aAppZihzPD1yKXtxPW0uYQpwPXEuVVoobCxzKQppZihwIT1udWxsKXttLmQ9
-cApzPXAuYgpvPXMuaW5kZXgKbj1vK3NbMF0ubGVuZ3RoCmlmKG89PT1uKXtpZihxLmIudW5pY29kZSl7
-cz1tLmMKcT1zKzEKaWYocTxyKXtzPUMueEIuTyhsLHMpCmlmKHM+PTU1Mjk2JiZzPD01NjMxOSl7cz1D
-LnhCLk8obCxxKQpzPXM+PTU2MzIwJiZzPD01NzM0M31lbHNlIHM9ITF9ZWxzZSBzPSExfWVsc2Ugcz0h
-MQpuPShzP24rMTpuKSsxfW0uYz1uCnJldHVybiEwfX1tLmI9bS5kPW51bGwKcmV0dXJuITF9LAokaUFu
-OjF9CkgudFEucHJvdG90eXBlPXsKcTpmdW5jdGlvbihhLGIpe0gudVAoYikKaWYoYiE9PTApSC52KFAu
-TzcoYixudWxsKSkKcmV0dXJuIHRoaXMuY30sCiRpT2Q6MX0KSC51bi5wcm90b3R5cGU9ewpnbTpmdW5j
-dGlvbihhKXtyZXR1cm4gbmV3IEguU2QodGhpcy5hLHRoaXMuYix0aGlzLmMpfX0KSC5TZC5wcm90b3R5
-cGU9ewpGOmZ1bmN0aW9uKCl7dmFyIHMscixxPXRoaXMscD1xLmMsbz1xLmIsbj1vLmxlbmd0aCxtPXEu
-YSxsPW0ubGVuZ3RoCmlmKHArbj5sKXtxLmQ9bnVsbApyZXR1cm4hMX1zPW0uaW5kZXhPZihvLHApCmlm
-KHM8MCl7cS5jPWwrMQpxLmQ9bnVsbApyZXR1cm4hMX1yPXMrbgpxLmQ9bmV3IEgudFEocyxvKQpxLmM9
-cj09PXEuYz9yKzE6cgpyZXR1cm4hMH0sCmdsOmZ1bmN0aW9uKCl7dmFyIHM9dGhpcy5kCnMudG9TdHJp
-bmcKcmV0dXJuIHN9LAokaUFuOjF9CkguRVQucHJvdG90eXBlPXskaUVUOjEsJGlBUzoxfQpILkxaLnBy
-b3RvdHlwZT17CmdBOmZ1bmN0aW9uKGEpe3JldHVybiBhLmxlbmd0aH0sCiRpWGo6MX0KSC5EZy5wcm90
-b3R5cGU9ewpxOmZ1bmN0aW9uKGEsYil7SC51UChiKQpILm9kKGIsYSxhLmxlbmd0aCkKcmV0dXJuIGFb
-Yl19LApZNTpmdW5jdGlvbihhLGIsYyl7SC5HSChjKQpILm9kKGIsYSxhLmxlbmd0aCkKYVtiXT1jfSwK
-JGliUToxLAokaWNYOjEsCiRpek06MX0KSC5QZy5wcm90b3R5cGU9ewpZNTpmdW5jdGlvbihhLGIsYyl7
-SC51UChjKQpILm9kKGIsYSxhLmxlbmd0aCkKYVtiXT1jfSwKJGliUToxLAokaWNYOjEsCiRpek06MX0K
-SC54ai5wcm90b3R5cGU9ewpxOmZ1bmN0aW9uKGEsYil7SC51UChiKQpILm9kKGIsYSxhLmxlbmd0aCkK
-cmV0dXJuIGFbYl19fQpILmRFLnByb3RvdHlwZT17CnE6ZnVuY3Rpb24oYSxiKXtILnVQKGIpCkgub2Qo
-YixhLGEubGVuZ3RoKQpyZXR1cm4gYVtiXX19CkguWkEucHJvdG90eXBlPXsKcTpmdW5jdGlvbihhLGIp
-e0gudVAoYikKSC5vZChiLGEsYS5sZW5ndGgpCnJldHVybiBhW2JdfX0KSC5kVC5wcm90b3R5cGU9ewpx
-OmZ1bmN0aW9uKGEsYil7SC51UChiKQpILm9kKGIsYSxhLmxlbmd0aCkKcmV0dXJuIGFbYl19fQpILlBx
-LnByb3RvdHlwZT17CnE6ZnVuY3Rpb24oYSxiKXtILnVQKGIpCkgub2QoYixhLGEubGVuZ3RoKQpyZXR1
-cm4gYVtiXX19CkguZUUucHJvdG90eXBlPXsKZ0E6ZnVuY3Rpb24oYSl7cmV0dXJuIGEubGVuZ3RofSwK
-cTpmdW5jdGlvbihhLGIpe0gudVAoYikKSC5vZChiLGEsYS5sZW5ndGgpCnJldHVybiBhW2JdfX0KSC5W
-Ni5wcm90b3R5cGU9ewpnQTpmdW5jdGlvbihhKXtyZXR1cm4gYS5sZW5ndGh9LApxOmZ1bmN0aW9uKGEs
-Yil7SC51UChiKQpILm9kKGIsYSxhLmxlbmd0aCkKcmV0dXJuIGFbYl19LAokaVY2OjEsCiRpbjY6MX0K
-SC5SRy5wcm90b3R5cGU9e30KSC5WUC5wcm90b3R5cGU9e30KSC5XQi5wcm90b3R5cGU9e30KSC5aRy5w
-cm90b3R5cGU9e30KSC5KYy5wcm90b3R5cGU9ewpDOmZ1bmN0aW9uKGEpe3JldHVybiBILmNFKHYudHlw
-ZVVuaXZlcnNlLHRoaXMsYSl9LApLcTpmdW5jdGlvbihhKXtyZXR1cm4gSC52NSh2LnR5cGVVbml2ZXJz
-ZSx0aGlzLGEpfX0KSC5HLnByb3RvdHlwZT17fQpILmxZLnByb3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7
-cmV0dXJuIEguZG0odGhpcy5hLG51bGwpfX0KSC5rUy5wcm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEpe3Jl
-dHVybiB0aGlzLmF9fQpILmlNLnByb3RvdHlwZT17fQpQLnRoLnByb3RvdHlwZT17CiQxOmZ1bmN0aW9u
-KGEpe3ZhciBzPXRoaXMuYSxyPXMuYQpzLmE9bnVsbApyLiQwKCl9LAokUzoxMH0KUC5oYS5wcm90b3R5
-cGU9ewokMTpmdW5jdGlvbihhKXt2YXIgcyxyCnRoaXMuYS5hPXQuTS5hKGEpCnM9dGhpcy5iCnI9dGhp
-cy5jCnMuZmlyc3RDaGlsZD9zLnJlbW92ZUNoaWxkKHIpOnMuYXBwZW5kQ2hpbGQocil9LAokUzo1M30K
-UC5Wcy5wcm90b3R5cGU9ewokMDpmdW5jdGlvbigpe3RoaXMuYS4kMCgpfSwKJEM6IiQwIiwKJFI6MCwK
-JFM6Mn0KUC5GdC5wcm90b3R5cGU9ewokMDpmdW5jdGlvbigpe3RoaXMuYS4kMCgpfSwKJEM6IiQwIiwK
-JFI6MCwKJFM6Mn0KUC5XMy5wcm90b3R5cGU9ewpDWTpmdW5jdGlvbihhLGIpe2lmKHNlbGYuc2V0VGlt
-ZW91dCE9bnVsbClzZWxmLnNldFRpbWVvdXQoSC50UihuZXcgUC55SCh0aGlzLGIpLDApLGEpCmVsc2Ug
-dGhyb3cgSC5iKFAuTDQoImBzZXRUaW1lb3V0KClgIG5vdCBmb3VuZC4iKSl9fQpQLnlILnByb3RvdHlw
-ZT17CiQwOmZ1bmN0aW9uKCl7dGhpcy5iLiQwKCl9LAokQzoiJDAiLAokUjowLAokUzowfQpQLmloLnBy
-b3RvdHlwZT17CmFNOmZ1bmN0aW9uKGEsYil7dmFyIHMscj10aGlzLHE9ci4kdGkKcS5DKCIxLz8iKS5h
-KGIpCmlmKCFyLmIpci5hLlhmKGIpCmVsc2V7cz1yLmEKaWYocS5DKCJiODwxPiIpLmIoYikpcy5jVShi
-KQplbHNlIHMuWDIocS5jLmEoYikpfX0sCncwOmZ1bmN0aW9uKGEsYil7dmFyIHMKaWYoYj09bnVsbCli
-PVAudjAoYSkKcz10aGlzLmEKaWYodGhpcy5iKXMuWkwoYSxiKQplbHNlIHMuTmsoYSxiKX19ClAuV00u
-cHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuYS4kMigwLGEpfSwKJFM6NDN9ClAu
-U1gucHJvdG90eXBlPXsKJDI6ZnVuY3Rpb24oYSxiKXt0aGlzLmEuJDIoMSxuZXcgSC5icShhLHQubC5h
-KGIpKSl9LAokQzoiJDIiLAokUjoyLAokUzo1NX0KUC5Hcy5wcm90b3R5cGU9ewokMjpmdW5jdGlvbihh
-LGIpe3RoaXMuYShILnVQKGEpLGIpfSwKJFM6MjR9ClAuRnkucHJvdG90eXBlPXsKdzpmdW5jdGlvbihh
-KXtyZXR1cm4iSXRlcmF0aW9uTWFya2VyKCIrdGhpcy5iKyIsICIrSC5Faih0aGlzLmEpKyIpIn19ClAu
-R1YucHJvdG90eXBlPXsKZ2w6ZnVuY3Rpb24oKXt2YXIgcz10aGlzLmMKaWYocz09bnVsbClyZXR1cm4g
-dGhpcy4kdGkuYy5hKHRoaXMuYikKcmV0dXJuIHMuZ2woKX0sCkY6ZnVuY3Rpb24oKXt2YXIgcyxyLHEs
-cCxvLG4sbT10aGlzCmZvcihzPW0uJHRpLkMoIkFuPDE+Iik7ITA7KXtyPW0uYwppZihyIT1udWxsKWlm
-KHIuRigpKXJldHVybiEwCmVsc2UgbS5zWDkobnVsbCkKcT1mdW5jdGlvbihhLGIsYyl7dmFyIGwsaz1i
-CndoaWxlKHRydWUpdHJ5e3JldHVybiBhKGssbCl9Y2F0Y2goail7bD1qCms9Y319KG0uYSwwLDEpCmlm
-KHEgaW5zdGFuY2VvZiBQLkZ5KXtwPXEuYgppZihwPT09Mil7bz1tLmQKaWYobz09bnVsbHx8by5sZW5n
-dGg9PT0wKXttLnNFQyhudWxsKQpyZXR1cm4hMX1pZigwPj1vLmxlbmd0aClyZXR1cm4gSC5PSChvLC0x
-KQptLmE9by5wb3AoKQpjb250aW51ZX1lbHNle3I9cS5hCmlmKHA9PT0zKXRocm93IHIKZWxzZXtuPXMu
-YShKLklUKHIpKQppZihuIGluc3RhbmNlb2YgUC5HVil7cj1tLmQKaWYocj09bnVsbClyPW0uZD1bXQpD
-Lk5tLmkocixtLmEpCm0uYT1uLmEKY29udGludWV9ZWxzZXttLnNYOShuKQpjb250aW51ZX19fX1lbHNl
-e20uc0VDKHEpCnJldHVybiEwfX1yZXR1cm4hMX0sCnNFQzpmdW5jdGlvbihhKXt0aGlzLmI9dGhpcy4k
-dGkuQygiMT8iKS5hKGEpfSwKc1g5OmZ1bmN0aW9uKGEpe3RoaXMuYz10aGlzLiR0aS5DKCJBbjwxPj8i
-KS5hKGEpfSwKJGlBbjoxfQpQLnE0LnByb3RvdHlwZT17CmdtOmZ1bmN0aW9uKGEpe3JldHVybiBuZXcg
-UC5HVih0aGlzLmEoKSx0aGlzLiR0aS5DKCJHVjwxPiIpKX19ClAuQ3cucHJvdG90eXBlPXsKdzpmdW5j
-dGlvbihhKXtyZXR1cm4gSC5Faih0aGlzLmEpfSwKJGlYUzoxLApnSUk6ZnVuY3Rpb24oKXtyZXR1cm4g
-dGhpcy5ifX0KUC5QZi5wcm90b3R5cGU9ewp3MDpmdW5jdGlvbihhLGIpe3ZhciBzCkguY2IoYSwiZXJy
-b3IiLHQuSykKcz10aGlzLmEKaWYocy5hIT09MCl0aHJvdyBILmIoUC5QVigiRnV0dXJlIGFscmVhZHkg
-Y29tcGxldGVkIikpCmlmKGI9PW51bGwpYj1QLnYwKGEpCnMuTmsoYSxiKX0sCnBtOmZ1bmN0aW9uKGEp
-e3JldHVybiB0aGlzLncwKGEsbnVsbCl9fQpQLlpmLnByb3RvdHlwZT17CmFNOmZ1bmN0aW9uKGEsYil7
-dmFyIHMscj10aGlzLiR0aQpyLkMoIjEvPyIpLmEoYikKcz10aGlzLmEKaWYocy5hIT09MCl0aHJvdyBI
-LmIoUC5QVigiRnV0dXJlIGFscmVhZHkgY29tcGxldGVkIikpCnMuWGYoci5DKCIxLyIpLmEoYikpfX0K
-UC5GZS5wcm90b3R5cGU9ewpIUjpmdW5jdGlvbihhKXtpZigodGhpcy5jJjE1KSE9PTYpcmV0dXJuITAK
-cmV0dXJuIHRoaXMuYi5iLmJ2KHQuYWwuYSh0aGlzLmQpLGEuYSx0LnksdC5LKX0sCkt3OmZ1bmN0aW9u
-KGEpe3ZhciBzPXRoaXMuZSxyPXQueixxPXQuSyxwPXRoaXMuJHRpLkMoIjIvIiksbz10aGlzLmIuYgpp
-Zih0LmFnLmIocykpcmV0dXJuIHAuYShvLnJwKHMsYS5hLGEuYixyLHEsdC5sKSkKZWxzZSByZXR1cm4g
-cC5hKG8uYnYodC5iSS5hKHMpLGEuYSxyLHEpKX19ClAudnMucHJvdG90eXBlPXsKU3E6ZnVuY3Rpb24o
-YSxiLGMpe3ZhciBzLHIscSxwPXRoaXMuJHRpCnAuS3EoYykuQygiMS8oMikiKS5hKGEpCnM9JC5YMwpp
-ZihzIT09Qy5OVSl7Yy5DKCJAPDAvPiIpLktxKHAuYykuQygiMSgyKSIpLmEoYSkKaWYoYiE9bnVsbCli
-PVAuVkgoYixzKX1yPW5ldyBQLnZzKHMsYy5DKCJ2czwwPiIpKQpxPWI9PW51bGw/MTozCnRoaXMueGYo
-bmV3IFAuRmUocixxLGEsYixwLkMoIkA8MT4iKS5LcShjKS5DKCJGZTwxLDI+IikpKQpyZXR1cm4gcn0s
-Clc3OmZ1bmN0aW9uKGEsYil7cmV0dXJuIHRoaXMuU3EoYSxudWxsLGIpfSwKUWQ6ZnVuY3Rpb24oYSxi
-LGMpe3ZhciBzLHI9dGhpcy4kdGkKci5LcShjKS5DKCIxLygyKSIpLmEoYSkKcz1uZXcgUC52cygkLlgz
-LGMuQygidnM8MD4iKSkKdGhpcy54ZihuZXcgUC5GZShzLDE5LGEsYixyLkMoIkA8MT4iKS5LcShjKS5D
-KCJGZTwxLDI+IikpKQpyZXR1cm4gc30sCnhmOmZ1bmN0aW9uKGEpe3ZhciBzLHI9dGhpcyxxPXIuYQpp
-ZihxPD0xKXthLmE9dC5GLmEoci5jKQpyLmM9YX1lbHNle2lmKHE9PT0yKXtzPXQuYy5hKHIuYykKcT1z
-LmEKaWYocTw0KXtzLnhmKGEpCnJldHVybn1yLmE9cQpyLmM9cy5jfVAuVGsobnVsbCxudWxsLHIuYix0
-Lk0uYShuZXcgUC5kYShyLGEpKSl9fSwKalE6ZnVuY3Rpb24oYSl7dmFyIHMscixxLHAsbyxuLG09dGhp
-cyxsPXt9CmwuYT1hCmlmKGE9PW51bGwpcmV0dXJuCnM9bS5hCmlmKHM8PTEpe3I9dC5GLmEobS5jKQpt
-LmM9YQppZihyIT1udWxsKXtxPWEuYQpmb3IocD1hO3EhPW51bGw7cD1xLHE9bylvPXEuYQpwLmE9cn19
-ZWxzZXtpZihzPT09Mil7bj10LmMuYShtLmMpCnM9bi5hCmlmKHM8NCl7bi5qUShhKQpyZXR1cm59bS5h
-PXMKbS5jPW4uY31sLmE9bS5OOChhKQpQLlRrKG51bGwsbnVsbCxtLmIsdC5NLmEobmV3IFAub1EobCxt
-KSkpfX0sCmFoOmZ1bmN0aW9uKCl7dmFyIHM9dC5GLmEodGhpcy5jKQp0aGlzLmM9bnVsbApyZXR1cm4g
-dGhpcy5OOChzKX0sCk44OmZ1bmN0aW9uKGEpe3ZhciBzLHIscQpmb3Iocz1hLHI9bnVsbDtzIT1udWxs
-O3I9cyxzPXEpe3E9cy5hCnMuYT1yfXJldHVybiByfSwKZWM6ZnVuY3Rpb24oYSl7dmFyIHMscixxLHA9
-dGhpcwpwLmE9MQp0cnl7YS5TcShuZXcgUC5wVihwKSxuZXcgUC5VNyhwKSx0LlApfWNhdGNoKHEpe3M9
-SC5SdShxKQpyPUgudHMocSkKUC5yYihuZXcgUC52cihwLHMscikpfX0sClgyOmZ1bmN0aW9uKGEpe3Zh
-ciBzLHI9dGhpcwpyLiR0aS5jLmEoYSkKcz1yLmFoKCkKci5hPTQKci5jPWEKUC5IWihyLHMpfSwKWkw6
-ZnVuY3Rpb24oYSxiKXt2YXIgcyxyLHE9dGhpcwp0LmwuYShiKQpzPXEuYWgoKQpyPVAuVGwoYSxiKQpx
-LmE9OApxLmM9cgpQLkhaKHEscyl9LApYZjpmdW5jdGlvbihhKXt2YXIgcz10aGlzLiR0aQpzLkMoIjEv
-IikuYShhKQppZihzLkMoImI4PDE+IikuYihhKSl7dGhpcy5jVShhKQpyZXR1cm59dGhpcy53VShzLmMu
-YShhKSl9LAp3VTpmdW5jdGlvbihhKXt2YXIgcz10aGlzCnMuJHRpLmMuYShhKQpzLmE9MQpQLlRrKG51
-bGwsbnVsbCxzLmIsdC5NLmEobmV3IFAucnQocyxhKSkpfSwKY1U6ZnVuY3Rpb24oYSl7dmFyIHM9dGhp
-cyxyPXMuJHRpCnIuQygiYjg8MT4iKS5hKGEpCmlmKHIuYihhKSl7aWYoYS5hPT09OCl7cy5hPTEKUC5U
-ayhudWxsLG51bGwscy5iLHQuTS5hKG5ldyBQLktGKHMsYSkpKX1lbHNlIFAuQTkoYSxzKQpyZXR1cm59
-cy5lYyhhKX0sCk5rOmZ1bmN0aW9uKGEsYil7dGhpcy5hPTEKUC5UayhudWxsLG51bGwsdGhpcy5iLHQu
-TS5hKG5ldyBQLlpMKHRoaXMsYSxiKSkpfSwKJGliODoxfQpQLmRhLnByb3RvdHlwZT17CiQwOmZ1bmN0
-aW9uKCl7UC5IWih0aGlzLmEsdGhpcy5iKX0sCiRTOjB9ClAub1EucHJvdG90eXBlPXsKJDA6ZnVuY3Rp
-b24oKXtQLkhaKHRoaXMuYix0aGlzLmEuYSl9LAokUzowfQpQLnBWLnByb3RvdHlwZT17CiQxOmZ1bmN0
-aW9uKGEpe3ZhciBzLHIscSxwPXRoaXMuYQpwLmE9MAp0cnl7cC5YMihwLiR0aS5jLmEoYSkpfWNhdGNo
-KHEpe3M9SC5SdShxKQpyPUgudHMocSkKcC5aTChzLHIpfX0sCiRTOjEwfQpQLlU3LnByb3RvdHlwZT17
-CiQyOmZ1bmN0aW9uKGEsYil7dGhpcy5hLlpMKGEsdC5sLmEoYikpfSwKJEM6IiQyIiwKJFI6MiwKJFM6
-Mjh9ClAudnIucHJvdG90eXBlPXsKJDA6ZnVuY3Rpb24oKXt0aGlzLmEuWkwodGhpcy5iLHRoaXMuYyl9
-LAokUzowfQpQLnJ0LnByb3RvdHlwZT17CiQwOmZ1bmN0aW9uKCl7dGhpcy5hLlgyKHRoaXMuYil9LAok
-UzowfQpQLktGLnByb3RvdHlwZT17CiQwOmZ1bmN0aW9uKCl7UC5BOSh0aGlzLmIsdGhpcy5hKX0sCiRT
-OjB9ClAuWkwucHJvdG90eXBlPXsKJDA6ZnVuY3Rpb24oKXt0aGlzLmEuWkwodGhpcy5iLHRoaXMuYyl9
-LAokUzowfQpQLlJULnByb3RvdHlwZT17CiQwOmZ1bmN0aW9uKCl7dmFyIHMscixxLHAsbyxuLG09dGhp
-cyxsPW51bGwKdHJ5e3E9bS5hLmEKbD1xLmIuYi56eih0LmZPLmEocS5kKSx0LnopfWNhdGNoKHApe3M9
-SC5SdShwKQpyPUgudHMocCkKaWYobS5jKXtxPXQubi5hKG0uYi5hLmMpLmEKbz1zCm89cT09bnVsbD9v
-PT1udWxsOnE9PT1vCnE9b31lbHNlIHE9ITEKbz1tLmEKaWYocSlvLmM9dC5uLmEobS5iLmEuYykKZWxz
-ZSBvLmM9UC5UbChzLHIpCm8uYj0hMApyZXR1cm59aWYobCBpbnN0YW5jZW9mIFAudnMmJmwuYT49NCl7
-aWYobC5hPT09OCl7cT1tLmEKcS5jPXQubi5hKGwuYykKcS5iPSEwfXJldHVybn1pZih0LmUuYihsKSl7
-bj1tLmIuYQpxPW0uYQpxLmM9bC5XNyhuZXcgUC5qWihuKSx0LnopCnEuYj0hMX19LAokUzowfQpQLmpa
-LnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmF9LAokUzoyOX0KUC5ycS5wcm90
-b3R5cGU9ewokMDpmdW5jdGlvbigpe3ZhciBzLHIscSxwLG8sbixtLGwKdHJ5e3E9dGhpcy5hCnA9cS5h
-Cm89cC4kdGkKbj1vLmMKbT1uLmEodGhpcy5iKQpxLmM9cC5iLmIuYnYoby5DKCIyLygxKSIpLmEocC5k
-KSxtLG8uQygiMi8iKSxuKX1jYXRjaChsKXtzPUguUnUobCkKcj1ILnRzKGwpCnE9dGhpcy5hCnEuYz1Q
-LlRsKHMscikKcS5iPSEwfX0sCiRTOjB9ClAuUlcucHJvdG90eXBlPXsKJDA6ZnVuY3Rpb24oKXt2YXIg
-cyxyLHEscCxvLG4sbSxsLGs9dGhpcwp0cnl7cz10Lm4uYShrLmEuYS5jKQpwPWsuYgppZihILm9UKHAu
-YS5IUihzKSkmJnAuYS5lIT1udWxsKXtwLmM9cC5hLkt3KHMpCnAuYj0hMX19Y2F0Y2gobyl7cj1ILlJ1
-KG8pCnE9SC50cyhvKQpwPXQubi5hKGsuYS5hLmMpCm49cC5hCm09cgpsPWsuYgppZihuPT1udWxsP209
-PW51bGw6bj09PW0pbC5jPXAKZWxzZSBsLmM9UC5UbChyLHEpCmwuYj0hMH19LAokUzowfQpQLk9NLnBy
-b3RvdHlwZT17fQpQLnFoLnByb3RvdHlwZT17CmdBOmZ1bmN0aW9uKGEpe3ZhciBzLHIscT10aGlzLHA9
-e30sbz1uZXcgUC52cygkLlgzLHQuZkopCnAuYT0wCnM9SC5MaChxKQpyPXMuQygifigxKT8iKS5hKG5l
-dyBQLkI1KHAscSkpCnQuWi5hKG5ldyBQLnVPKHAsbykpClcuSkUocS5hLHEuYixyLCExLHMuYykKcmV0
-dXJuIG99fQpQLkI1LnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe0guTGgodGhpcy5iKS5jLmEoYSk7
-Kyt0aGlzLmEuYX0sCiRTOmZ1bmN0aW9uKCl7cmV0dXJuIEguTGgodGhpcy5iKS5DKCJ+KDEpIil9fQpQ
-LnVPLnByb3RvdHlwZT17CiQwOmZ1bmN0aW9uKCl7dmFyIHM9dGhpcy5iLHI9cy4kdGkscT1yLkMoIjEv
-IikuYSh0aGlzLmEuYSkscD1zLmFoKCkKci5jLmEocSkKcy5hPTQKcy5jPXEKUC5IWihzLHApfSwKJFM6
-MH0KUC5NTy5wcm90b3R5cGU9e30KUC5rVC5wcm90b3R5cGU9e30KUC54SS5wcm90b3R5cGU9e30KUC5t
-MC5wcm90b3R5cGU9eyRpUW06MX0KUC5Fdi5wcm90b3R5cGU9ewokMDpmdW5jdGlvbigpe3ZhciBzPUgu
-Yih0aGlzLmEpCnMuc3RhY2s9Si53KHRoaXMuYikKdGhyb3cgc30sCiRTOjB9ClAuSmkucHJvdG90eXBl
-PXsKYkg6ZnVuY3Rpb24oYSl7dmFyIHMscixxCnQuTS5hKGEpCnRyeXtpZihDLk5VPT09JC5YMyl7YS4k
-MCgpCnJldHVybn1QLlQ4KG51bGwsbnVsbCx0aGlzLGEsdC5IKX1jYXRjaChxKXtzPUguUnUocSkKcj1I
-LnRzKHEpClAuU2kocyx0LmwuYShyKSl9fSwKRGw6ZnVuY3Rpb24oYSxiLGMpe3ZhciBzLHIscQpjLkMo
-In4oMCkiKS5hKGEpCmMuYShiKQp0cnl7aWYoQy5OVT09PSQuWDMpe2EuJDEoYikKcmV0dXJufVAueXYo
-bnVsbCxudWxsLHRoaXMsYSxiLHQuSCxjKX1jYXRjaChxKXtzPUguUnUocSkKcj1ILnRzKHEpClAuU2ko
-cyx0LmwuYShyKSl9fSwKR1k6ZnVuY3Rpb24oYSl7cmV0dXJuIG5ldyBQLlZwKHRoaXMsdC5NLmEoYSkp
-fSwKUHk6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gbmV3IFAuT1IodGhpcyxiLkMoIn4oMCkiKS5hKGEpLGIp
-fSwKcTpmdW5jdGlvbihhLGIpe3JldHVybiBudWxsfSwKeno6ZnVuY3Rpb24oYSxiKXtiLkMoIjAoKSIp
-LmEoYSkKaWYoJC5YMz09PUMuTlUpcmV0dXJuIGEuJDAoKQpyZXR1cm4gUC5UOChudWxsLG51bGwsdGhp
-cyxhLGIpfSwKYnY6ZnVuY3Rpb24oYSxiLGMsZCl7Yy5DKCJAPDA+IikuS3EoZCkuQygiMSgyKSIpLmEo
-YSkKZC5hKGIpCmlmKCQuWDM9PT1DLk5VKXJldHVybiBhLiQxKGIpCnJldHVybiBQLnl2KG51bGwsbnVs
-bCx0aGlzLGEsYixjLGQpfSwKcnA6ZnVuY3Rpb24oYSxiLGMsZCxlLGYpe2QuQygiQDwwPiIpLktxKGUp
-LktxKGYpLkMoIjEoMiwzKSIpLmEoYSkKZS5hKGIpCmYuYShjKQppZigkLlgzPT09Qy5OVSlyZXR1cm4g
-YS4kMihiLGMpCnJldHVybiBQLlF4KG51bGwsbnVsbCx0aGlzLGEsYixjLGQsZSxmKX0sCkxqOmZ1bmN0
-aW9uKGEsYixjLGQpe3JldHVybiBiLkMoIkA8MD4iKS5LcShjKS5LcShkKS5DKCIxKDIsMykiKS5hKGEp
-fX0KUC5WcC5wcm90b3R5cGU9ewokMDpmdW5jdGlvbigpe3JldHVybiB0aGlzLmEuYkgodGhpcy5iKX0s
-CiRTOjB9ClAuT1IucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7dmFyIHM9dGhpcy5jCnJldHVybiB0
-aGlzLmEuRGwodGhpcy5iLHMuYShhKSxzKX0sCiRTOmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuYy5DKCJ+
-KDApIil9fQpQLmI2LnByb3RvdHlwZT17CmdtOmZ1bmN0aW9uKGEpe3ZhciBzPXRoaXMscj1uZXcgUC5s
-bShzLHMucixILkxoKHMpLkMoImxtPDE+IikpCnIuYz1zLmUKcmV0dXJuIHJ9LApnQTpmdW5jdGlvbihh
-KXtyZXR1cm4gdGhpcy5hfSwKZ2wwOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmE9PT0wfSwKZ29yOmZ1
-bmN0aW9uKGEpe3JldHVybiB0aGlzLmEhPT0wfSwKdGc6ZnVuY3Rpb24oYSxiKXt2YXIgcyxyCmlmKHR5
-cGVvZiBiPT0ic3RyaW5nIiYmYiE9PSJfX3Byb3RvX18iKXtzPXRoaXMuYgppZihzPT1udWxsKXJldHVy
-biExCnJldHVybiB0Lm0uYShzW2JdKSE9bnVsbH1lbHNle3I9dGhpcy5QUihiKQpyZXR1cm4gcn19LApQ
-UjpmdW5jdGlvbihhKXt2YXIgcz10aGlzLmQKaWYocz09bnVsbClyZXR1cm4hMQpyZXR1cm4gdGhpcy5E
-RihzW3RoaXMuTihhKV0sYSk+PTB9LAppOmZ1bmN0aW9uKGEsYil7dmFyIHMscixxPXRoaXMKSC5MaChx
-KS5jLmEoYikKaWYodHlwZW9mIGI9PSJzdHJpbmciJiZiIT09Il9fcHJvdG9fXyIpe3M9cS5iCnJldHVy
-biBxLmJRKHM9PW51bGw/cS5iPVAuVDIoKTpzLGIpfWVsc2UgaWYodHlwZW9mIGI9PSJudW1iZXIiJiYo
-YiYxMDczNzQxODIzKT09PWIpe3I9cS5jCnJldHVybiBxLmJRKHI9PW51bGw/cS5jPVAuVDIoKTpyLGIp
-fWVsc2UgcmV0dXJuIHEuQjcoYil9LApCNzpmdW5jdGlvbihhKXt2YXIgcyxyLHEscD10aGlzCkguTGgo
-cCkuYy5hKGEpCnM9cC5kCmlmKHM9PW51bGwpcz1wLmQ9UC5UMigpCnI9cC5OKGEpCnE9c1tyXQppZihx
-PT1udWxsKXNbcl09W3AueW8oYSldCmVsc2V7aWYocC5ERihxLGEpPj0wKXJldHVybiExCnEucHVzaChw
-LnlvKGEpKX1yZXR1cm4hMH0sClI6ZnVuY3Rpb24oYSxiKXt2YXIgcz10aGlzCmlmKHR5cGVvZiBiPT0i
-c3RyaW5nIiYmYiE9PSJfX3Byb3RvX18iKXJldHVybiBzLkgocy5iLGIpCmVsc2UgaWYodHlwZW9mIGI9
-PSJudW1iZXIiJiYoYiYxMDczNzQxODIzKT09PWIpcmV0dXJuIHMuSChzLmMsYikKZWxzZSByZXR1cm4g
-cy5xZyhiKX0sCnFnOmZ1bmN0aW9uKGEpe3ZhciBzLHIscSxwLG89dGhpcyxuPW8uZAppZihuPT1udWxs
-KXJldHVybiExCnM9by5OKGEpCnI9bltzXQpxPW8uREYocixhKQppZihxPDApcmV0dXJuITEKcD1yLnNw
-bGljZShxLDEpWzBdCmlmKDA9PT1yLmxlbmd0aClkZWxldGUgbltzXQpvLkcocCkKcmV0dXJuITB9LApi
-UTpmdW5jdGlvbihhLGIpe0guTGgodGhpcykuYy5hKGIpCmlmKHQubS5hKGFbYl0pIT1udWxsKXJldHVy
-biExCmFbYl09dGhpcy55byhiKQpyZXR1cm4hMH0sCkg6ZnVuY3Rpb24oYSxiKXt2YXIgcwppZihhPT1u
-dWxsKXJldHVybiExCnM9dC5tLmEoYVtiXSkKaWYocz09bnVsbClyZXR1cm4hMQp0aGlzLkcocykKZGVs
-ZXRlIGFbYl0KcmV0dXJuITB9LApTOmZ1bmN0aW9uKCl7dGhpcy5yPXRoaXMucisxJjEwNzM3NDE4MjN9
-LAp5bzpmdW5jdGlvbihhKXt2YXIgcyxyPXRoaXMscT1uZXcgUC5ibihILkxoKHIpLmMuYShhKSkKaWYo
-ci5lPT1udWxsKXIuZT1yLmY9cQplbHNle3M9ci5mCnMudG9TdHJpbmcKcS5jPXMKci5mPXMuYj1xfSsr
-ci5hCnIuUygpCnJldHVybiBxfSwKRzpmdW5jdGlvbihhKXt2YXIgcz10aGlzLHI9YS5jLHE9YS5iCmlm
-KHI9PW51bGwpcy5lPXEKZWxzZSByLmI9cQppZihxPT1udWxsKXMuZj1yCmVsc2UgcS5jPXI7LS1zLmEK
-cy5TKCl9LApOOmZ1bmN0aW9uKGEpe3JldHVybiBKLmhmKGEpJjEwNzM3NDE4MjN9LApERjpmdW5jdGlv
-bihhLGIpe3ZhciBzLHIKaWYoYT09bnVsbClyZXR1cm4tMQpzPWEubGVuZ3RoCmZvcihyPTA7cjxzOysr
-cilpZihKLlJNKGFbcl0uYSxiKSlyZXR1cm4gcgpyZXR1cm4tMX19ClAuYm4ucHJvdG90eXBlPXt9ClAu
-bG0ucHJvdG90eXBlPXsKZ2w6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5kfSwKRjpmdW5jdGlvbigpe3Zh
-ciBzPXRoaXMscj1zLmMscT1zLmEKaWYocy5iIT09cS5yKXRocm93IEguYihQLmE0KHEpKQplbHNlIGlm
-KHI9PW51bGwpe3Muc2oobnVsbCkKcmV0dXJuITF9ZWxzZXtzLnNqKHMuJHRpLkMoIjE/IikuYShyLmEp
-KQpzLmM9ci5iCnJldHVybiEwfX0sCnNqOmZ1bmN0aW9uKGEpe3RoaXMuZD10aGlzLiR0aS5DKCIxPyIp
-LmEoYSl9LAokaUFuOjF9ClAubVcucHJvdG90eXBlPXt9ClAudXkucHJvdG90eXBlPXskaWJROjEsJGlj
-WDoxLCRpek06MX0KUC5sRC5wcm90b3R5cGU9ewpnbTpmdW5jdGlvbihhKXtyZXR1cm4gbmV3IEguYTco
-YSx0aGlzLmdBKGEpLEgueksoYSkuQygiYTc8bEQuRT4iKSl9LApFOmZ1bmN0aW9uKGEsYil7cmV0dXJu
-IHRoaXMucShhLGIpfSwKSzpmdW5jdGlvbihhLGIpe3ZhciBzLHIKSC56SyhhKS5DKCJ+KGxELkUpIiku
-YShiKQpzPXRoaXMuZ0EoYSkKZm9yKHI9MDtyPHM7KytyKXtiLiQxKHRoaXMucShhLHIpKQppZihzIT09
-dGhpcy5nQShhKSl0aHJvdyBILmIoUC5hNChhKSl9fSwKZ2wwOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlz
-LmdBKGEpPT09MH0sCmdvcjpmdW5jdGlvbihhKXtyZXR1cm4hdGhpcy5nbDAoYSl9LApFMjpmdW5jdGlv
-bihhLGIsYyl7dmFyIHM9SC56SyhhKQpyZXR1cm4gbmV3IEgubEooYSxzLktxKGMpLkMoIjEobEQuRSki
-KS5hKGIpLHMuQygiQDxsRC5FPiIpLktxKGMpLkMoImxKPDEsMj4iKSl9LAplUjpmdW5jdGlvbihhLGIp
-e3JldHVybiBILnFDKGEsYixudWxsLEgueksoYSkuQygibEQuRSIpKX0sCmRyOmZ1bmN0aW9uKGEsYil7
-cmV0dXJuIG5ldyBILmpWKGEsSC56SyhhKS5DKCJAPGxELkU+IikuS3EoYikuQygialY8MSwyPiIpKX0s
-CmR1OmZ1bmN0aW9uKGEsYixjLGQpe3ZhciBzCkgueksoYSkuQygibEQuRT8iKS5hKGQpClAuakIoYixj
-LHRoaXMuZ0EoYSkpCmZvcihzPWI7czxjOysrcyl0aGlzLlk1KGEscyxkKX0sCnc6ZnVuY3Rpb24oYSl7
-cmV0dXJuIFAueChhLCJbIiwiXSIpfX0KUC5pbC5wcm90b3R5cGU9e30KUC5yYS5wcm90b3R5cGU9ewok
-MjpmdW5jdGlvbihhLGIpe3ZhciBzLHI9dGhpcy5hCmlmKCFyLmEpdGhpcy5iLmErPSIsICIKci5hPSEx
-CnI9dGhpcy5iCnM9ci5hKz1ILkVqKGEpCnIuYT1zKyI6ICIKci5hKz1ILkVqKGIpfSwKJFM6MTF9ClAu
-WWsucHJvdG90eXBlPXsKSzpmdW5jdGlvbihhLGIpe3ZhciBzLHIKSC5MaCh0aGlzKS5DKCJ+KFlrLkss
-WWsuVikiKS5hKGIpCmZvcihzPUouSVQodGhpcy5ndmMoKSk7cy5GKCk7KXtyPXMuZ2woKQpiLiQyKHIs
-dGhpcy5xKDAscikpfX0sCmdQdTpmdW5jdGlvbihhKXtyZXR1cm4gSi5NMSh0aGlzLmd2YygpLG5ldyBQ
-LnlRKHRoaXMpLEguTGgodGhpcykuQygiTjM8WWsuSyxZay5WPiIpKX0sCng0OmZ1bmN0aW9uKGEpe3Jl
-dHVybiBKLnpsKHRoaXMuZ3ZjKCksYSl9LApnQTpmdW5jdGlvbihhKXtyZXR1cm4gSi5IbSh0aGlzLmd2
-YygpKX0sCmdsMDpmdW5jdGlvbihhKXtyZXR1cm4gSi51VSh0aGlzLmd2YygpKX0sCnc6ZnVuY3Rpb24o
-YSl7cmV0dXJuIFAubk8odGhpcyl9LAokaVowOjF9ClAueVEucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24o
-YSl7dmFyIHM9dGhpcy5hLHI9SC5MaChzKQpyLkMoIllrLksiKS5hKGEpCnJldHVybiBuZXcgUC5OMyhh
-LHMucSgwLGEpLHIuQygiQDxZay5LPiIpLktxKHIuQygiWWsuViIpKS5DKCJOMzwxLDI+IikpfSwKJFM6
-ZnVuY3Rpb24oKXtyZXR1cm4gSC5MaCh0aGlzLmEpLkMoIk4zPFlrLkssWWsuVj4oWWsuSykiKX19ClAu
-S1AucHJvdG90eXBlPXsKWTU6ZnVuY3Rpb24oYSxiLGMpe3ZhciBzPUguTGgodGhpcykKcy5jLmEoYikK
-cy5RWzFdLmEoYykKdGhyb3cgSC5iKFAuTDQoIkNhbm5vdCBtb2RpZnkgdW5tb2RpZmlhYmxlIG1hcCIp
-KX19ClAuUG4ucHJvdG90eXBlPXsKcTpmdW5jdGlvbihhLGIpe3JldHVybiB0aGlzLmEucSgwLGIpfSwK
-WTU6ZnVuY3Rpb24oYSxiLGMpe3ZhciBzPUguTGgodGhpcykKdGhpcy5hLlk1KDAscy5jLmEoYikscy5R
-WzFdLmEoYykpfSwKeDQ6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuYS54NChhKX0sCks6ZnVuY3Rpb24o
-YSxiKXt0aGlzLmEuSygwLEguTGgodGhpcykuQygifigxLDIpIikuYShiKSl9LApnbDA6ZnVuY3Rpb24o
-YSl7dmFyIHM9dGhpcy5hCnJldHVybiBzLmdsMChzKX0sCmdBOmZ1bmN0aW9uKGEpe3ZhciBzPXRoaXMu
-YQpyZXR1cm4gcy5nQShzKX0sCnc6ZnVuY3Rpb24oYSl7cmV0dXJuIEoudyh0aGlzLmEpfSwKZ1B1OmZ1
-bmN0aW9uKGEpe3ZhciBzPXRoaXMuYQpyZXR1cm4gcy5nUHUocyl9LAokaVowOjF9ClAuR2oucHJvdG90
-eXBlPXt9ClAubGYucHJvdG90eXBlPXsKZ2wwOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmdBKHRoaXMp
-PT09MH0sCmdvcjpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5nQSh0aGlzKSE9PTB9LApGVjpmdW5jdGlv
-bihhLGIpe3ZhciBzCmZvcihzPUouSVQoSC5MaCh0aGlzKS5DKCJjWDxsZi5FPiIpLmEoYikpO3MuRigp
-Oyl0aGlzLmkoMCxzLmdsKCkpfSwKdzpmdW5jdGlvbihhKXtyZXR1cm4gUC54KHRoaXMsInsiLCJ9Iil9
-LAprOmZ1bmN0aW9uKGEsYil7dmFyIHMscj10aGlzLmdtKHRoaXMpCmlmKCFyLkYoKSlyZXR1cm4iIgpp
-ZihiPT09IiIpe3M9IiIKZG8gcys9SC5FaihyLmQpCndoaWxlKHIuRigpKX1lbHNle3M9SC5FaihyLmQp
-CmZvcig7ci5GKCk7KXM9cytiK0guRWooci5kKX1yZXR1cm4gcy5jaGFyQ29kZUF0KDApPT0wP3M6c30s
-CmVSOmZ1bmN0aW9uKGEsYil7cmV0dXJuIEguYksodGhpcyxiLEguTGgodGhpcykuQygibGYuRSIpKX0s
-CkU6ZnVuY3Rpb24oYSxiKXt2YXIgcyxyLHEscD0iaW5kZXgiCkguY2IoYixwLHQuUykKUC5rMShiLHAp
-CmZvcihzPXRoaXMuZ20odGhpcykscj0wO3MuRigpOyl7cT1zLmQKaWYoYj09PXIpcmV0dXJuIHE7Kyty
-fXRocm93IEguYihQLkNmKGIsdGhpcyxwLG51bGwscikpfX0KUC5Wai5wcm90b3R5cGU9eyRpYlE6MSwk
-aWNYOjEsJGl4dToxfQpQLlh2LnByb3RvdHlwZT17JGliUToxLCRpY1g6MSwkaXh1OjF9ClAublkucHJv
-dG90eXBlPXt9ClAuV1kucHJvdG90eXBlPXt9ClAuUlUucHJvdG90eXBlPXt9ClAucFIucHJvdG90eXBl
-PXt9ClAudXcucHJvdG90eXBlPXsKcTpmdW5jdGlvbihhLGIpe3ZhciBzLHI9dGhpcy5iCmlmKHI9PW51
-bGwpcmV0dXJuIHRoaXMuYy5xKDAsYikKZWxzZSBpZih0eXBlb2YgYiE9InN0cmluZyIpcmV0dXJuIG51
-bGwKZWxzZXtzPXJbYl0KcmV0dXJuIHR5cGVvZiBzPT0idW5kZWZpbmVkIj90aGlzLmZiKGIpOnN9fSwK
-Z0E6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuYj09bnVsbD90aGlzLmMuYTp0aGlzLkNmKCkubGVuZ3Ro
-fSwKZ2wwOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmdBKHRoaXMpPT09MH0sCmd2YzpmdW5jdGlvbigp
-e2lmKHRoaXMuYj09bnVsbCl7dmFyIHM9dGhpcy5jCnJldHVybiBuZXcgSC5pNShzLEguTGgocykuQygi
-aTU8MT4iKSl9cmV0dXJuIG5ldyBQLmk4KHRoaXMpfSwKWTU6ZnVuY3Rpb24oYSxiLGMpe3ZhciBzLHIs
-cT10aGlzCmlmKHEuYj09bnVsbClxLmMuWTUoMCxiLGMpCmVsc2UgaWYocS54NChiKSl7cz1xLmIKc1ti
-XT1jCnI9cS5hCmlmKHI9PW51bGw/cyE9bnVsbDpyIT09cylyW2JdPW51bGx9ZWxzZSBxLlhLKCkuWTUo
-MCxiLGMpfSwKeDQ6ZnVuY3Rpb24oYSl7aWYodGhpcy5iPT1udWxsKXJldHVybiB0aGlzLmMueDQoYSkK
-cmV0dXJuIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0aGlzLmEsYSl9LApLOmZ1
-bmN0aW9uKGEsYil7dmFyIHMscixxLHAsbz10aGlzCnQuY0EuYShiKQppZihvLmI9PW51bGwpcmV0dXJu
-IG8uYy5LKDAsYikKcz1vLkNmKCkKZm9yKHI9MDtyPHMubGVuZ3RoOysrcil7cT1zW3JdCnA9by5iW3Fd
-CmlmKHR5cGVvZiBwPT0idW5kZWZpbmVkIil7cD1QLlFlKG8uYVtxXSkKby5iW3FdPXB9Yi4kMihxLHAp
-CmlmKHMhPT1vLmMpdGhyb3cgSC5iKFAuYTQobykpfX0sCkNmOmZ1bmN0aW9uKCl7dmFyIHM9dC5iTS5h
-KHRoaXMuYykKaWYocz09bnVsbClzPXRoaXMuYz1ILlFJKE9iamVjdC5rZXlzKHRoaXMuYSksdC5zKQpy
-ZXR1cm4gc30sClhLOmZ1bmN0aW9uKCl7dmFyIHMscixxLHAsbyxuPXRoaXMKaWYobi5iPT1udWxsKXJl
-dHVybiBuLmMKcz1QLkZsKHQuTix0LnopCnI9bi5DZigpCmZvcihxPTA7cD1yLmxlbmd0aCxxPHA7Kytx
-KXtvPXJbcV0Kcy5ZNSgwLG8sbi5xKDAsbykpfWlmKHA9PT0wKUMuTm0uaShyLCIiKQplbHNlIEMuTm0u
-c0EociwwKQpuLmE9bi5iPW51bGwKcmV0dXJuIG4uYz1zfSwKZmI6ZnVuY3Rpb24oYSl7dmFyIHMKaWYo
-IU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0aGlzLmEsYSkpcmV0dXJuIG51bGwK
-cz1QLlFlKHRoaXMuYVthXSkKcmV0dXJuIHRoaXMuYlthXT1zfX0KUC5pOC5wcm90b3R5cGU9ewpnQTpm
-dW5jdGlvbihhKXt2YXIgcz10aGlzLmEKcmV0dXJuIHMuZ0Eocyl9LApFOmZ1bmN0aW9uKGEsYil7dmFy
-IHM9dGhpcy5hCmlmKHMuYj09bnVsbClzPXMuZ3ZjKCkuRSgwLGIpCmVsc2V7cz1zLkNmKCkKaWYoYjww
-fHxiPj1zLmxlbmd0aClyZXR1cm4gSC5PSChzLGIpCnM9c1tiXX1yZXR1cm4gc30sCmdtOmZ1bmN0aW9u
-KGEpe3ZhciBzPXRoaXMuYQppZihzLmI9PW51bGwpe3M9cy5ndmMoKQpzPXMuZ20ocyl9ZWxzZXtzPXMu
-Q2YoKQpzPW5ldyBKLm0xKHMscy5sZW5ndGgsSC50NihzKS5DKCJtMTwxPiIpKX1yZXR1cm4gc30sCnRn
-OmZ1bmN0aW9uKGEsYil7cmV0dXJuIHRoaXMuYS54NChiKX19ClAueHIucHJvdG90eXBlPXsKJDA6ZnVu
-Y3Rpb24oKXt2YXIgcyxyCnRyeXtzPW5ldyBUZXh0RGVjb2RlcigidXRmLTgiLHtmYXRhbDp0cnVlfSkK
-cmV0dXJuIHN9Y2F0Y2gocil7SC5SdShyKX1yZXR1cm4gbnVsbH0sCiRTOjEyfQpQLk56LnByb3RvdHlw
-ZT17CiQwOmZ1bmN0aW9uKCl7dmFyIHMscgp0cnl7cz1uZXcgVGV4dERlY29kZXIoInV0Zi04Iix7ZmF0
-YWw6ZmFsc2V9KQpyZXR1cm4gc31jYXRjaChyKXtILlJ1KHIpfXJldHVybiBudWxsfSwKJFM6MTJ9ClAu
-Q1YucHJvdG90eXBlPXsKeXI6ZnVuY3Rpb24oYTAsYTEsYTIpe3ZhciBzLHIscSxwLG8sbixtLGwsayxq
-LGksaCxnLGYsZSxkLGMsYixhPSJJbnZhbGlkIGJhc2U2NCBlbmNvZGluZyBsZW5ndGggIgphMj1QLmpC
-KGExLGEyLGEwLmxlbmd0aCkKcz0kLlY3KCkKZm9yKHI9YTEscT1yLHA9bnVsbCxvPS0xLG49LTEsbT0w
-O3I8YTI7cj1sKXtsPXIrMQprPUMueEIuVyhhMCxyKQppZihrPT09Mzcpe2o9bCsyCmlmKGo8PWEyKXtp
-PUgub28oQy54Qi5XKGEwLGwpKQpoPUgub28oQy54Qi5XKGEwLGwrMSkpCmc9aSoxNitoLShoJjI1NikK
-aWYoZz09PTM3KWc9LTEKbD1qfWVsc2UgZz0tMX1lbHNlIGc9awppZigwPD1nJiZnPD0xMjcpe2lmKGc8
-MHx8Zz49cy5sZW5ndGgpcmV0dXJuIEguT0gocyxnKQpmPXNbZ10KaWYoZj49MCl7Zz1DLnhCLk8oIkFC
-Q0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXowMTIzNDU2Nzg5
-Ky8iLGYpCmlmKGc9PT1rKWNvbnRpbnVlCms9Z31lbHNle2lmKGY9PT0tMSl7aWYobzwwKXtlPXA9PW51
-bGw/bnVsbDpwLmEubGVuZ3RoCmlmKGU9PW51bGwpZT0wCm89ZSsoci1xKQpuPXJ9KyttCmlmKGs9PT02
-MSljb250aW51ZX1rPWd9aWYoZiE9PS0yKXtpZihwPT1udWxsKXtwPW5ldyBQLk0oIiIpCmU9cH1lbHNl
-IGU9cApkPWUuYSs9Qy54Qi5OaihhMCxxLHIpCmUuYT1kK0guTHcoaykKcT1sCmNvbnRpbnVlfX10aHJv
-dyBILmIoUC5ycigiSW52YWxpZCBiYXNlNjQgZGF0YSIsYTAscikpfWlmKHAhPW51bGwpe2U9cC5hKz1D
-LnhCLk5qKGEwLHEsYTIpCmQ9ZS5sZW5ndGgKaWYobz49MClQLnhNKGEwLG4sYTIsbyxtLGQpCmVsc2V7
-Yz1DLmpuLnpZKGQtMSw0KSsxCmlmKGM9PT0xKXRocm93IEguYihQLnJyKGEsYTAsYTIpKQpmb3IoO2M8
-NDspe2UrPSI9IgpwLmE9ZTsrK2N9fWU9cC5hCnJldHVybiBDLnhCLmk3KGEwLGExLGEyLGUuY2hhckNv
-ZGVBdCgwKT09MD9lOmUpfWI9YTItYTEKaWYobz49MClQLnhNKGEwLG4sYTIsbyxtLGIpCmVsc2V7Yz1D
-LmpuLnpZKGIsNCkKaWYoYz09PTEpdGhyb3cgSC5iKFAucnIoYSxhMCxhMikpCmlmKGM+MSlhMD1DLnhC
-Lmk3KGEwLGEyLGEyLGM9PT0yPyI9PSI6Ij0iKX1yZXR1cm4gYTB9fQpQLlU4LnByb3RvdHlwZT17fQpQ
-LlVrLnByb3RvdHlwZT17fQpQLndJLnByb3RvdHlwZT17fQpQLlppLnByb3RvdHlwZT17fQpQLlVkLnBy
-b3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7dmFyIHM9UC5obCh0aGlzLmEpCnJldHVybih0aGlzLmIhPW51
-bGw/IkNvbnZlcnRpbmcgb2JqZWN0IHRvIGFuIGVuY29kYWJsZSBvYmplY3QgZmFpbGVkOiI6IkNvbnZl
-cnRpbmcgb2JqZWN0IGRpZCBub3QgcmV0dXJuIGFuIGVuY29kYWJsZSBvYmplY3Q6IikrIiAiK3N9fQpQ
-Lks4LnByb3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7cmV0dXJuIkN5Y2xpYyBlcnJvciBpbiBKU09OIHN0
-cmluZ2lmeSJ9fQpQLmJ5LnByb3RvdHlwZT17CnBXOmZ1bmN0aW9uKGEsYixjKXt2YXIgcwp0LmZWLmEo
-YykKcz1QLkJTKGIsdGhpcy5nSGUoKS5hKQpyZXR1cm4gc30sCk9COmZ1bmN0aW9uKGEsYil7dmFyIHMK
-dC5kQS5hKGIpCnM9UC51WChhLHRoaXMuZ1pFKCkuYixudWxsKQpyZXR1cm4gc30sCmdaRTpmdW5jdGlv
-bigpe3JldHVybiBDLm5YfSwKZ0hlOmZ1bmN0aW9uKCl7cmV0dXJuIEMuQTN9fQpQLm9qLnByb3RvdHlw
-ZT17fQpQLk14LnByb3RvdHlwZT17fQpQLlNoLnByb3RvdHlwZT17ClJUOmZ1bmN0aW9uKGEpe3ZhciBz
-LHIscSxwLG8sbixtLGw9YS5sZW5ndGgKZm9yKHM9Si5yWShhKSxyPXRoaXMuYyxxPTAscD0wO3A8bDsr
-K3Ape289cy5XKGEscCkKaWYobz45Mil7aWYobz49NTUyOTYpe249byY2NDUxMgppZihuPT09NTUyOTYp
-e209cCsxCm09IShtPGwmJihDLnhCLlcoYSxtKSY2NDUxMik9PT01NjMyMCl9ZWxzZSBtPSExCmlmKCFt
-KWlmKG49PT01NjMyMCl7bj1wLTEKbj0hKG4+PTAmJihDLnhCLk8oYSxuKSY2NDUxMik9PT01NTI5Nil9
-ZWxzZSBuPSExCmVsc2Ugbj0hMAppZihuKXtpZihwPnEpci5hKz1DLnhCLk5qKGEscSxwKQpxPXArMQpu
-PXIuYSs9SC5Mdyg5MikKbis9SC5MdygxMTcpCnIuYT1uCm4rPUguTHcoMTAwKQpyLmE9bgptPW8+Pj44
-JjE1Cm4rPUguTHcobTwxMD80OCttOjg3K20pCnIuYT1uCm09bz4+PjQmMTUKbis9SC5MdyhtPDEwPzQ4
-K206ODcrbSkKci5hPW4KbT1vJjE1CnIuYT1uK0guTHcobTwxMD80OCttOjg3K20pfX1jb250aW51ZX1p
-ZihvPDMyKXtpZihwPnEpci5hKz1DLnhCLk5qKGEscSxwKQpxPXArMQpuPXIuYSs9SC5Mdyg5MikKc3dp
-dGNoKG8pe2Nhc2UgODpyLmE9bitILkx3KDk4KQpicmVhawpjYXNlIDk6ci5hPW4rSC5MdygxMTYpCmJy
-ZWFrCmNhc2UgMTA6ci5hPW4rSC5MdygxMTApCmJyZWFrCmNhc2UgMTI6ci5hPW4rSC5MdygxMDIpCmJy
-ZWFrCmNhc2UgMTM6ci5hPW4rSC5MdygxMTQpCmJyZWFrCmRlZmF1bHQ6bis9SC5MdygxMTcpCnIuYT1u
-Cm4rPUguTHcoNDgpCnIuYT1uCm4rPUguTHcoNDgpCnIuYT1uCm09bz4+PjQmMTUKbis9SC5MdyhtPDEw
-PzQ4K206ODcrbSkKci5hPW4KbT1vJjE1CnIuYT1uK0guTHcobTwxMD80OCttOjg3K20pCmJyZWFrfX1l
-bHNlIGlmKG89PT0zNHx8bz09PTkyKXtpZihwPnEpci5hKz1DLnhCLk5qKGEscSxwKQpxPXArMQpuPXIu
-YSs9SC5Mdyg5MikKci5hPW4rSC5MdyhvKX19aWYocT09PTApci5hKz1ILkVqKGEpCmVsc2UgaWYocTxs
-KXIuYSs9cy5OaihhLHEsbCl9LApKbjpmdW5jdGlvbihhKXt2YXIgcyxyLHEscApmb3Iocz10aGlzLmEs
-cj1zLmxlbmd0aCxxPTA7cTxyOysrcSl7cD1zW3FdCmlmKGE9PW51bGw/cD09bnVsbDphPT09cCl0aHJv
-dyBILmIobmV3IFAuSzgoYSxudWxsKSl9Qy5ObS5pKHMsYSl9LAppVTpmdW5jdGlvbihhKXt2YXIgcyxy
-LHEscCxvPXRoaXMKaWYoby50TShhKSlyZXR1cm4Kby5KbihhKQp0cnl7cz1vLmIuJDEoYSkKaWYoIW8u
-dE0ocykpe3E9UC5HeShhLG51bGwsby5nVksoKSkKdGhyb3cgSC5iKHEpfXE9by5hCmlmKDA+PXEubGVu
-Z3RoKXJldHVybiBILk9IKHEsLTEpCnEucG9wKCl9Y2F0Y2gocCl7cj1ILlJ1KHApCnE9UC5HeShhLHIs
-by5nVksoKSkKdGhyb3cgSC5iKHEpfX0sCnRNOmZ1bmN0aW9uKGEpe3ZhciBzLHIscT10aGlzCmlmKHR5
-cGVvZiBhPT0ibnVtYmVyIil7aWYoIWlzRmluaXRlKGEpKXJldHVybiExCnEuYy5hKz1DLkNELncoYSkK
-cmV0dXJuITB9ZWxzZSBpZihhPT09ITApe3EuYy5hKz0idHJ1ZSIKcmV0dXJuITB9ZWxzZSBpZihhPT09
-ITEpe3EuYy5hKz0iZmFsc2UiCnJldHVybiEwfWVsc2UgaWYoYT09bnVsbCl7cS5jLmErPSJudWxsIgpy
-ZXR1cm4hMH1lbHNlIGlmKHR5cGVvZiBhPT0ic3RyaW5nIil7cz1xLmMKcy5hKz0nIicKcS5SVChhKQpz
-LmErPSciJwpyZXR1cm4hMH1lbHNlIGlmKHQuai5iKGEpKXtxLkpuKGEpCnEubEsoYSkKcz1xLmEKaWYo
-MD49cy5sZW5ndGgpcmV0dXJuIEguT0gocywtMSkKcy5wb3AoKQpyZXR1cm4hMH1lbHNlIGlmKHQuZi5i
-KGEpKXtxLkpuKGEpCnI9cS5qdyhhKQpzPXEuYQppZigwPj1zLmxlbmd0aClyZXR1cm4gSC5PSChzLC0x
-KQpzLnBvcCgpCnJldHVybiByfWVsc2UgcmV0dXJuITF9LApsSzpmdW5jdGlvbihhKXt2YXIgcyxyLHE9
-dGhpcy5jCnEuYSs9IlsiCnM9Si5VNihhKQppZihzLmdvcihhKSl7dGhpcy5pVShzLnEoYSwwKSkKZm9y
-KHI9MTtyPHMuZ0EoYSk7KytyKXtxLmErPSIsIgp0aGlzLmlVKHMucShhLHIpKX19cS5hKz0iXSJ9LApq
-dzpmdW5jdGlvbihhKXt2YXIgcyxyLHEscCxvLG4sbT10aGlzLGw9e30KaWYoYS5nbDAoYSkpe20uYy5h
-Kz0ie30iCnJldHVybiEwfXM9YS5nQShhKSoyCnI9UC5POChzLG51bGwsITEsdC5XKQpxPWwuYT0wCmwu
-Yj0hMAphLksoMCxuZXcgUC50aShsLHIpKQppZighbC5iKXJldHVybiExCnA9bS5jCnAuYSs9InsiCmZv
-cihvPSciJztxPHM7cSs9MixvPScsIicpe3AuYSs9bwptLlJUKEguaChyW3FdKSkKcC5hKz0nIjonCm49
-cSsxCmlmKG4+PXMpcmV0dXJuIEguT0gocixuKQptLmlVKHJbbl0pfXAuYSs9In0iCnJldHVybiEwfX0K
-UC50aS5wcm90b3R5cGU9ewokMjpmdW5jdGlvbihhLGIpe3ZhciBzLHIKaWYodHlwZW9mIGEhPSJzdHJp
-bmciKXRoaXMuYS5iPSExCnM9dGhpcy5iCnI9dGhpcy5hCkMuTm0uWTUocyxyLmErKyxhKQpDLk5tLlk1
-KHMsci5hKyssYil9LAokUzoxMX0KUC50dS5wcm90b3R5cGU9ewpnVks6ZnVuY3Rpb24oKXt2YXIgcz10
-aGlzLmMuYQpyZXR1cm4gcy5jaGFyQ29kZUF0KDApPT0wP3M6c319ClAudTUucHJvdG90eXBlPXsKZ1pF
-OmZ1bmN0aW9uKCl7cmV0dXJuIEMuUWt9fQpQLkUzLnByb3RvdHlwZT17CldKOmZ1bmN0aW9uKGEpe3Zh
-ciBzLHIscSxwPVAuakIoMCxudWxsLGEubGVuZ3RoKSxvPXAtMAppZihvPT09MClyZXR1cm4gbmV3IFVp
-bnQ4QXJyYXkoMCkKcz1vKjMKcj1uZXcgVWludDhBcnJheShzKQpxPW5ldyBQLlJ3KHIpCmlmKHEuR3go
-YSwwLHApIT09cCl7Si5hNihhLHAtMSkKcS5STygpfXJldHVybiBuZXcgVWludDhBcnJheShyLnN1YmFy
-cmF5KDAsSC5yTSgwLHEuYixzKSkpfX0KUC5Sdy5wcm90b3R5cGU9ewpSTzpmdW5jdGlvbigpe3ZhciBz
-PXRoaXMscj1zLmMscT1zLmIscD1zLmI9cSsxLG89ci5sZW5ndGgKaWYocT49bylyZXR1cm4gSC5PSChy
-LHEpCnJbcV09MjM5CnE9cy5iPXArMQppZihwPj1vKXJldHVybiBILk9IKHIscCkKcltwXT0xOTEKcy5i
-PXErMQppZihxPj1vKXJldHVybiBILk9IKHIscSkKcltxXT0xODl9LApPNjpmdW5jdGlvbihhLGIpe3Zh
-ciBzLHIscSxwLG8sbj10aGlzCmlmKChiJjY0NTEyKT09PTU2MzIwKXtzPTY1NTM2KygoYSYxMDIzKTw8
-MTApfGImMTAyMwpyPW4uYwpxPW4uYgpwPW4uYj1xKzEKbz1yLmxlbmd0aAppZihxPj1vKXJldHVybiBI
-Lk9IKHIscSkKcltxXT1zPj4+MTh8MjQwCnE9bi5iPXArMQppZihwPj1vKXJldHVybiBILk9IKHIscCkK
-cltwXT1zPj4+MTImNjN8MTI4CnA9bi5iPXErMQppZihxPj1vKXJldHVybiBILk9IKHIscSkKcltxXT1z
-Pj4+NiY2M3wxMjgKbi5iPXArMQppZihwPj1vKXJldHVybiBILk9IKHIscCkKcltwXT1zJjYzfDEyOApy
-ZXR1cm4hMH1lbHNle24uUk8oKQpyZXR1cm4hMX19LApHeDpmdW5jdGlvbihhLGIsYyl7dmFyIHMscixx
-LHAsbyxuLG0sbD10aGlzCmlmKGIhPT1jJiYoQy54Qi5PKGEsYy0xKSY2NDUxMik9PT01NTI5NiktLWMK
-Zm9yKHM9bC5jLHI9cy5sZW5ndGgscT1iO3E8YzsrK3Epe3A9Qy54Qi5XKGEscSkKaWYocDw9MTI3KXtv
-PWwuYgppZihvPj1yKWJyZWFrCmwuYj1vKzEKc1tvXT1wfWVsc2V7bz1wJjY0NTEyCmlmKG89PT01NTI5
-Nil7aWYobC5iKzQ+cilicmVhawpuPXErMQppZihsLk82KHAsQy54Qi5XKGEsbikpKXE9bn1lbHNlIGlm
-KG89PT01NjMyMCl7aWYobC5iKzM+cilicmVhawpsLlJPKCl9ZWxzZSBpZihwPD0yMDQ3KXtvPWwuYgpt
-PW8rMQppZihtPj1yKWJyZWFrCmwuYj1tCmlmKG8+PXIpcmV0dXJuIEguT0gocyxvKQpzW29dPXA+Pj42
-fDE5MgpsLmI9bSsxCnNbbV09cCY2M3wxMjh9ZWxzZXtvPWwuYgppZihvKzI+PXIpYnJlYWsKbT1sLmI9
-bysxCmlmKG8+PXIpcmV0dXJuIEguT0gocyxvKQpzW29dPXA+Pj4xMnwyMjQKbz1sLmI9bSsxCmlmKG0+
-PXIpcmV0dXJuIEguT0gocyxtKQpzW21dPXA+Pj42JjYzfDEyOApsLmI9bysxCmlmKG8+PXIpcmV0dXJu
-IEguT0gocyxvKQpzW29dPXAmNjN8MTI4fX19cmV0dXJuIHF9fQpQLkdZLnByb3RvdHlwZT17CldKOmZ1
-bmN0aW9uKGEpe3ZhciBzLHIKdC5MLmEoYSkKcz10aGlzLmEKcj1QLmt5KHMsYSwwLG51bGwpCmlmKHIh
-PW51bGwpcmV0dXJuIHIKcmV0dXJuIG5ldyBQLmJ6KHMpLk5lKGEsMCxudWxsLCEwKX19ClAuYnoucHJv
-dG90eXBlPXsKTmU6ZnVuY3Rpb24oYSxiLGMsZCl7dmFyIHMscixxLHAsbyxuPXRoaXMKdC5MLmEoYSkK
-cz1QLmpCKGIsYyxKLkhtKGEpKQppZihiPT09cylyZXR1cm4iIgpyPVAuankoYSxiLHMpCnE9bi5oTyhy
-LDAscy1iLCEwKQpwPW4uYgppZigocCYxKSE9PTApe289UC5qNChwKQpuLmI9MAp0aHJvdyBILmIoUC5y
-cihvLGEsYituLmMpKX1yZXR1cm4gcX0sCmhPOmZ1bmN0aW9uKGEsYixjLGQpe3ZhciBzLHIscT10aGlz
-CmlmKGMtYj4xMDAwKXtzPUMuam4uQlUoYitjLDIpCnI9cS5oTyhhLGIscywhMSkKaWYoKHEuYiYxKSE9
-PTApcmV0dXJuIHIKcmV0dXJuIHIrcS5oTyhhLHMsYyxkKX1yZXR1cm4gcS5FaChhLGIsYyxkKX0sCkVo
-OmZ1bmN0aW9uKGEsYixjLGQpe3ZhciBzLHIscSxwLG8sbixtLGwsaz10aGlzLGo9NjU1MzMsaT1rLmIs
-aD1rLmMsZz1uZXcgUC5NKCIiKSxmPWIrMSxlPWEubGVuZ3RoCmlmKGI8MHx8Yj49ZSlyZXR1cm4gSC5P
-SChhLGIpCnM9YVtiXQokbGFiZWwwJDA6Zm9yKHI9ay5hOyEwOyl7Zm9yKDshMDtmPW8pe3E9Qy54Qi5X
-KCJBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB
+e3ZhciBzPXRoaXMuYgppZihzPT1udWxsKXJldHVybiExCnJldHVybiB0aGlzLlh1KHMsYSl9LApxOmZ1
+bmN0aW9uKGEsYil7dmFyIHMscixxLHAsbz10aGlzLG49bnVsbAppZih0eXBlb2YgYj09InN0cmluZyIp
+e3M9by5iCmlmKHM9PW51bGwpcmV0dXJuIG4Kcj1vLmoyKHMsYikKcT1yPT1udWxsP246ci5iCnJldHVy
+biBxfWVsc2UgaWYodHlwZW9mIGI9PSJudW1iZXIiJiYoYiYweDNmZmZmZmYpPT09Yil7cD1vLmMKaWYo
+cD09bnVsbClyZXR1cm4gbgpyPW8uajIocCxiKQpxPXI9PW51bGw/bjpyLmIKcmV0dXJuIHF9ZWxzZSBy
+ZXR1cm4gby5hYShiKX0sCmFhOmZ1bmN0aW9uKGEpe3ZhciBzLHIscT10aGlzLmQKaWYocT09bnVsbCly
+ZXR1cm4gbnVsbApzPXRoaXMuQnQocSxKLmhmKGEpJjB4M2ZmZmZmZikKcj10aGlzLkZoKHMsYSkKaWYo
+cjwwKXJldHVybiBudWxsCnJldHVybiBzW3JdLmJ9LApZNTpmdW5jdGlvbihhLGIsYyl7dmFyIHMscixx
+LHAsbyxuLG09dGhpcyxsPUguTGgobSkKbC5jLmEoYikKbC5RWzFdLmEoYykKaWYodHlwZW9mIGI9PSJz
+dHJpbmciKXtzPW0uYgptLkVIKHM9PW51bGw/bS5iPW0ueksoKTpzLGIsYyl9ZWxzZSBpZih0eXBlb2Yg
+Yj09Im51bWJlciImJihiJjB4M2ZmZmZmZik9PT1iKXtyPW0uYwptLkVIKHI9PW51bGw/bS5jPW0uekso
+KTpyLGIsYyl9ZWxzZXtxPW0uZAppZihxPT1udWxsKXE9bS5kPW0ueksoKQpwPUouaGYoYikmMHgzZmZm
+ZmZmCm89bS5CdChxLHApCmlmKG89PW51bGwpbS5FSShxLHAsW20uSG4oYixjKV0pCmVsc2V7bj1tLkZo
+KG8sYikKaWYobj49MClvW25dLmI9YwplbHNlIG8ucHVzaChtLkhuKGIsYykpfX19LApLOmZ1bmN0aW9u
+KGEsYil7dmFyIHMscixxPXRoaXMKSC5MaChxKS5DKCJ+KDEsMikiKS5hKGIpCnM9cS5lCnI9cS5yCmZv
+cig7cyE9bnVsbDspe2IuJDIocy5hLHMuYikKaWYociE9PXEucil0aHJvdyBILmIoUC5hNChxKSkKcz1z
+LmN9fSwKRUg6ZnVuY3Rpb24oYSxiLGMpe3ZhciBzLHI9dGhpcyxxPUguTGgocikKcS5jLmEoYikKcS5R
+WzFdLmEoYykKcz1yLmoyKGEsYikKaWYocz09bnVsbClyLkVJKGEsYixyLkhuKGIsYykpCmVsc2Ugcy5i
+PWN9LAprczpmdW5jdGlvbigpe3RoaXMucj10aGlzLnIrMSY2NzEwODg2M30sCkhuOmZ1bmN0aW9uKGEs
+Yil7dmFyIHM9dGhpcyxyPUguTGgocykscT1uZXcgSC52aChyLmMuYShhKSxyLlFbMV0uYShiKSkKaWYo
+cy5lPT1udWxsKXMuZT1zLmY9cQplbHNle3I9cy5mCnIudG9TdHJpbmcKcS5kPXIKcy5mPXIuYz1xfSsr
+cy5hCnMua3MoKQpyZXR1cm4gcX0sCkZoOmZ1bmN0aW9uKGEsYil7dmFyIHMscgppZihhPT1udWxsKXJl
+dHVybi0xCnM9YS5sZW5ndGgKZm9yKHI9MDtyPHM7KytyKWlmKEouUk0oYVtyXS5hLGIpKXJldHVybiBy
+CnJldHVybi0xfSwKdzpmdW5jdGlvbihhKXtyZXR1cm4gUC5uTyh0aGlzKX0sCmoyOmZ1bmN0aW9uKGEs
+Yil7cmV0dXJuIGFbYl19LApCdDpmdW5jdGlvbihhLGIpe3JldHVybiBhW2JdfSwKRUk6ZnVuY3Rpb24o
+YSxiLGMpe2FbYl09Y30sCnJuOmZ1bmN0aW9uKGEsYil7ZGVsZXRlIGFbYl19LApYdTpmdW5jdGlvbihh
+LGIpe3JldHVybiB0aGlzLmoyKGEsYikhPW51bGx9LAp6SzpmdW5jdGlvbigpe3ZhciBzPSI8bm9uLWlk
+ZW50aWZpZXIta2V5PiIscj1PYmplY3QuY3JlYXRlKG51bGwpCnRoaXMuRUkocixzLHIpCnRoaXMucm4o
+cixzKQpyZXR1cm4gcn0sCiRpRm86MX0KSC52aC5wcm90b3R5cGU9e30KSC5pNS5wcm90b3R5cGU9ewpn
+QTpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5hLmF9LApnbDA6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMu
+YS5hPT09MH0sCmdtOmZ1bmN0aW9uKGEpe3ZhciBzPXRoaXMuYSxyPW5ldyBILk42KHMscy5yLHRoaXMu
+JHRpLkMoIk42PDE+IikpCnIuYz1zLmUKcmV0dXJuIHJ9LAp0ZzpmdW5jdGlvbihhLGIpe3JldHVybiB0
+aGlzLmEueDQoYil9fQpILk42LnByb3RvdHlwZT17CmdsOmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuZH0s
+CkY6ZnVuY3Rpb24oKXt2YXIgcyxyPXRoaXMscT1yLmEKaWYoci5iIT09cS5yKXRocm93IEguYihQLmE0
+KHEpKQpzPXIuYwppZihzPT1udWxsKXtyLnNxWShudWxsKQpyZXR1cm4hMX1lbHNle3Iuc3FZKHMuYSkK
+ci5jPXMuYwpyZXR1cm4hMH19LApzcVk6ZnVuY3Rpb24oYSl7dGhpcy5kPXRoaXMuJHRpLkMoIjE/Iiku
+YShhKX0sCiRpQW46MX0KSC5kQy5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5h
+KGEpfSwKJFM6NH0KSC53Ti5wcm90b3R5cGU9ewokMjpmdW5jdGlvbihhLGIpe3JldHVybiB0aGlzLmEo
+YSxiKX0sCiRTOjUwfQpILlZYLnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmEo
+SC5uKGEpKX0sCiRTOjIxfQpILlZSLnByb3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7cmV0dXJuIlJlZ0V4
+cC8iK3RoaXMuYSsiLyIrdGhpcy5iLmZsYWdzfSwKZ0hjOmZ1bmN0aW9uKCl7dmFyIHM9dGhpcyxyPXMu
+YwppZihyIT1udWxsKXJldHVybiByCnI9cy5iCnJldHVybiBzLmM9SC52NChzLmEsci5tdWx0aWxpbmUs
+IXIuaWdub3JlQ2FzZSxyLnVuaWNvZGUsci5kb3RBbGwsITApfSwKZGQ6ZnVuY3Rpb24oYSxiKXtyZXR1
+cm4gbmV3IEguS1codGhpcyxiLDApfSwKVVo6ZnVuY3Rpb24oYSxiKXt2YXIgcyxyPXQuSy5hKHRoaXMu
+Z0hjKCkpCnIubGFzdEluZGV4PWIKcz1yLmV4ZWMoYSkKaWYocz09bnVsbClyZXR1cm4gbnVsbApyZXR1
+cm4gbmV3IEguRUsocyl9LAokaXZYOjEsCiRpd0w6MX0KSC5FSy5wcm90b3R5cGU9ewpxOmZ1bmN0aW9u
+KGEsYil7dmFyIHMKSC5JWihiKQpzPXRoaXMuYgppZihiPj1zLmxlbmd0aClyZXR1cm4gSC5PSChzLGIp
+CnJldHVybiBzW2JdfSwKJGlPZDoxLAokaWliOjF9CkguS1cucHJvdG90eXBlPXsKZ206ZnVuY3Rpb24o
+YSl7cmV0dXJuIG5ldyBILlBiKHRoaXMuYSx0aGlzLmIsdGhpcy5jKX19CkguUGIucHJvdG90eXBlPXsK
+Z2w6ZnVuY3Rpb24oKXtyZXR1cm4gdC5jei5hKHRoaXMuZCl9LApGOmZ1bmN0aW9uKCl7dmFyIHMscixx
+LHAsbyxuLG09dGhpcyxsPW0uYgppZihsPT1udWxsKXJldHVybiExCnM9bS5jCnI9bC5sZW5ndGgKaWYo
+czw9cil7cT1tLmEKcD1xLlVaKGwscykKaWYocCE9bnVsbCl7bS5kPXAKcz1wLmIKbz1zLmluZGV4Cm49
+bytzWzBdLmxlbmd0aAppZihvPT09bil7aWYocS5iLnVuaWNvZGUpe3M9bS5jCnE9cysxCmlmKHE8cil7
+cz1DLnhCLk8obCxzKQppZihzPj01NTI5NiYmczw9NTYzMTkpe3M9Qy54Qi5PKGwscSkKcz1zPj01NjMy
+MCYmczw9NTczNDN9ZWxzZSBzPSExfWVsc2Ugcz0hMX1lbHNlIHM9ITEKbj0ocz9uKzE6bikrMX1tLmM9
+bgpyZXR1cm4hMH19bS5iPW0uZD1udWxsCnJldHVybiExfSwKJGlBbjoxfQpILnRRLnByb3RvdHlwZT17
+CnE6ZnVuY3Rpb24oYSxiKXtILklaKGIpCmlmKGIhPT0wKUgudihQLk83KGIsbnVsbCkpCnJldHVybiB0
+aGlzLmN9LAokaU9kOjF9CkgudW4ucHJvdG90eXBlPXsKZ206ZnVuY3Rpb24oYSl7cmV0dXJuIG5ldyBI
+LlNkKHRoaXMuYSx0aGlzLmIsdGhpcy5jKX19CkguU2QucHJvdG90eXBlPXsKRjpmdW5jdGlvbigpe3Zh
+ciBzLHIscT10aGlzLHA9cS5jLG89cS5iLG49by5sZW5ndGgsbT1xLmEsbD1tLmxlbmd0aAppZihwK24+
+bCl7cS5kPW51bGwKcmV0dXJuITF9cz1tLmluZGV4T2YobyxwKQppZihzPDApe3EuYz1sKzEKcS5kPW51
+bGwKcmV0dXJuITF9cj1zK24KcS5kPW5ldyBILnRRKHMsbykKcS5jPXI9PT1xLmM/cisxOnIKcmV0dXJu
+ITB9LApnbDpmdW5jdGlvbigpe3ZhciBzPXRoaXMuZApzLnRvU3RyaW5nCnJldHVybiBzfSwKJGlBbjox
+fQpILkVULnByb3RvdHlwZT17JGlFVDoxLCRpQVM6MX0KSC5MWi5wcm90b3R5cGU9ewpnQTpmdW5jdGlv
+bihhKXtyZXR1cm4gYS5sZW5ndGh9LAokaVhqOjF9CkguRGcucHJvdG90eXBlPXsKcTpmdW5jdGlvbihh
+LGIpe0guSVooYikKSC5vZChiLGEsYS5sZW5ndGgpCnJldHVybiBhW2JdfSwKWTU6ZnVuY3Rpb24oYSxi
+LGMpe0guclYoYykKSC5vZChiLGEsYS5sZW5ndGgpCmFbYl09Y30sCiRpYlE6MSwKJGljWDoxLAokaXpN
+OjF9CkguUGcucHJvdG90eXBlPXsKWTU6ZnVuY3Rpb24oYSxiLGMpe0guSVooYykKSC5vZChiLGEsYS5s
+ZW5ndGgpCmFbYl09Y30sCiRpYlE6MSwKJGljWDoxLAokaXpNOjF9CkgueGoucHJvdG90eXBlPXsKcTpm
+dW5jdGlvbihhLGIpe0guSVooYikKSC5vZChiLGEsYS5sZW5ndGgpCnJldHVybiBhW2JdfX0KSC5kRS5w
+cm90b3R5cGU9ewpxOmZ1bmN0aW9uKGEsYil7SC5JWihiKQpILm9kKGIsYSxhLmxlbmd0aCkKcmV0dXJu
+IGFbYl19fQpILlpBLnByb3RvdHlwZT17CnE6ZnVuY3Rpb24oYSxiKXtILklaKGIpCkgub2QoYixhLGEu
+bGVuZ3RoKQpyZXR1cm4gYVtiXX19CkguZFQucHJvdG90eXBlPXsKcTpmdW5jdGlvbihhLGIpe0guSVoo
+YikKSC5vZChiLGEsYS5sZW5ndGgpCnJldHVybiBhW2JdfX0KSC5QcS5wcm90b3R5cGU9ewpxOmZ1bmN0
+aW9uKGEsYil7SC5JWihiKQpILm9kKGIsYSxhLmxlbmd0aCkKcmV0dXJuIGFbYl19fQpILmVFLnByb3Rv
+dHlwZT17CmdBOmZ1bmN0aW9uKGEpe3JldHVybiBhLmxlbmd0aH0sCnE6ZnVuY3Rpb24oYSxiKXtILkla
+KGIpCkgub2QoYixhLGEubGVuZ3RoKQpyZXR1cm4gYVtiXX19CkguVjYucHJvdG90eXBlPXsKZ0E6ZnVu
+Y3Rpb24oYSl7cmV0dXJuIGEubGVuZ3RofSwKcTpmdW5jdGlvbihhLGIpe0guSVooYikKSC5vZChiLGEs
+YS5sZW5ndGgpCnJldHVybiBhW2JdfSwKJGluNjoxfQpILlJHLnByb3RvdHlwZT17fQpILlZQLnByb3Rv
+dHlwZT17fQpILldCLnByb3RvdHlwZT17fQpILlpHLnByb3RvdHlwZT17fQpILkpjLnByb3RvdHlwZT17
+CkM6ZnVuY3Rpb24oYSl7cmV0dXJuIEguY0Uodi50eXBlVW5pdmVyc2UsdGhpcyxhKX0sCktxOmZ1bmN0
+aW9uKGEpe3JldHVybiBILnY1KHYudHlwZVVuaXZlcnNlLHRoaXMsYSl9fQpILkcucHJvdG90eXBlPXt9
+Ckgua1MucHJvdG90eXBlPXsKdzpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5hfX0KSC5pTS5wcm90b3R5
+cGU9e30KUC50aC5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXt2YXIgcz10aGlzLmEscj1zLmEKcy5h
+PW51bGwKci4kMCgpfSwKJFM6MTN9ClAuaGEucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7dmFyIHMs
+cgp0aGlzLmEuYT10Lk0uYShhKQpzPXRoaXMuYgpyPXRoaXMuYwpzLmZpcnN0Q2hpbGQ/cy5yZW1vdmVD
+aGlsZChyKTpzLmFwcGVuZENoaWxkKHIpfSwKJFM6NDF9ClAuVnMucHJvdG90eXBlPXsKJDA6ZnVuY3Rp
+b24oKXt0aGlzLmEuJDAoKX0sCiRDOiIkMCIsCiRSOjAsCiRTOjE1fQpQLkZ0LnByb3RvdHlwZT17CiQw
+OmZ1bmN0aW9uKCl7dGhpcy5hLiQwKCl9LAokQzoiJDAiLAokUjowLAokUzoxNX0KUC5XMy5wcm90b3R5
+cGU9ewpDWTpmdW5jdGlvbihhLGIpe2lmKHNlbGYuc2V0VGltZW91dCE9bnVsbClzZWxmLnNldFRpbWVv
+dXQoSC50UihuZXcgUC55SCh0aGlzLGIpLDApLGEpCmVsc2UgdGhyb3cgSC5iKFAuTDQoImBzZXRUaW1l
+b3V0KClgIG5vdCBmb3VuZC4iKSl9fQpQLnlILnByb3RvdHlwZT17CiQwOmZ1bmN0aW9uKCl7dGhpcy5i
+LiQwKCl9LAokQzoiJDAiLAokUjowLAokUzowfQpQLmloLnByb3RvdHlwZT17CmFNOmZ1bmN0aW9uKGEs
+Yil7dmFyIHMscj10aGlzLHE9ci4kdGkKcS5DKCIxLz8iKS5hKGIpCmlmKGI9PW51bGwpYj1xLmMuYShi
+KQppZighci5iKXIuYS5YZihiKQplbHNle3M9ci5hCmlmKHEuQygiYjg8MT4iKS5iKGIpKXMuY1UoYikK
+ZWxzZSBzLlgyKHEuYy5hKGIpKX19LAp3MDpmdW5jdGlvbihhLGIpe3ZhciBzPXRoaXMuYQppZih0aGlz
+LmIpcy5aTChhLGIpCmVsc2Ugcy5OayhhLGIpfX0KUC5XTS5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihh
+KXtyZXR1cm4gdGhpcy5hLiQyKDAsYSl9LAokUzo1MX0KUC5TWC5wcm90b3R5cGU9ewokMjpmdW5jdGlv
+bihhLGIpe3RoaXMuYS4kMigxLG5ldyBILmJxKGEsdC5sLmEoYikpKX0sCiRDOiIkMiIsCiRSOjIsCiRT
+OjUzfQpQLkdzLnByb3RvdHlwZT17CiQyOmZ1bmN0aW9uKGEsYil7dGhpcy5hKEguSVooYSksYil9LAok
+Uzo0NX0KUC5GeS5wcm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEpe3JldHVybiJJdGVyYXRpb25NYXJrZXIo
+Iit0aGlzLmIrIiwgIitILkVqKHRoaXMuYSkrIikifX0KUC5HVi5wcm90b3R5cGU9ewpnbDpmdW5jdGlv
+bigpe3ZhciBzPXRoaXMuYwppZihzPT1udWxsKXJldHVybiB0aGlzLiR0aS5jLmEodGhpcy5iKQpyZXR1
+cm4gcy5nbCgpfSwKRjpmdW5jdGlvbigpe3ZhciBzLHIscSxwLG8sbixtPXRoaXMKZm9yKHM9bS4kdGku
+QygiQW48MT4iKTshMDspe3I9bS5jCmlmKHIhPW51bGwpaWYoci5GKCkpcmV0dXJuITAKZWxzZSBtLnNY
+OShudWxsKQpxPWZ1bmN0aW9uKGEsYixjKXt2YXIgbCxrPWIKd2hpbGUodHJ1ZSl0cnl7cmV0dXJuIGEo
+ayxsKX1jYXRjaChqKXtsPWoKaz1jfX0obS5hLDAsMSkKaWYocSBpbnN0YW5jZW9mIFAuRnkpe3A9cS5i
+CmlmKHA9PT0yKXtvPW0uZAppZihvPT1udWxsfHxvLmxlbmd0aD09PTApe20uc0VDKG51bGwpCnJldHVy
+biExfWlmKDA+PW8ubGVuZ3RoKXJldHVybiBILk9IKG8sLTEpCm0uYT1vLnBvcCgpCmNvbnRpbnVlfWVs
+c2V7cj1xLmEKaWYocD09PTMpdGhyb3cgcgplbHNle249cy5hKEouSVQocikpCmlmKG4gaW5zdGFuY2Vv
+ZiBQLkdWKXtyPW0uZAppZihyPT1udWxsKXI9bS5kPVtdCkMuTm0uaShyLG0uYSkKbS5hPW4uYQpjb250
+aW51ZX1lbHNle20uc1g5KG4pCmNvbnRpbnVlfX19fWVsc2V7bS5zRUMocSkKcmV0dXJuITB9fXJldHVy
+biExfSwKc0VDOmZ1bmN0aW9uKGEpe3RoaXMuYj10aGlzLiR0aS5DKCIxPyIpLmEoYSl9LApzWDk6ZnVu
+Y3Rpb24oYSl7dGhpcy5jPXRoaXMuJHRpLkMoIkFuPDE+PyIpLmEoYSl9LAokaUFuOjF9ClAucTQucHJv
+dG90eXBlPXsKZ206ZnVuY3Rpb24oYSl7cmV0dXJuIG5ldyBQLkdWKHRoaXMuYSgpLHRoaXMuJHRpLkMo
+IkdWPDE+IikpfX0KUC5Ddy5wcm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEpe3JldHVybiBILkVqKHRoaXMu
+YSl9LAokaVhTOjEsCmdJSTpmdW5jdGlvbigpe3JldHVybiB0aGlzLmJ9fQpQLlBmLnByb3RvdHlwZT17
+CncwOmZ1bmN0aW9uKGEsYil7dmFyIHMKSC5jYihhLCJlcnJvciIsdC5LKQpzPXRoaXMuYQppZihzLmEh
+PT0wKXRocm93IEguYihQLlBWKCJGdXR1cmUgYWxyZWFkeSBjb21wbGV0ZWQiKSkKaWYoYj09bnVsbCli
+PVAudjAoYSkKcy5OayhhLGIpfSwKcG06ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMudzAoYSxudWxsKX19
+ClAuWmYucHJvdG90eXBlPXsKYU06ZnVuY3Rpb24oYSxiKXt2YXIgcyxyPXRoaXMuJHRpCnIuQygiMS8/
+IikuYShiKQpzPXRoaXMuYQppZihzLmEhPT0wKXRocm93IEguYihQLlBWKCJGdXR1cmUgYWxyZWFkeSBj
+b21wbGV0ZWQiKSkKcy5YZihyLkMoIjEvIikuYShiKSl9fQpQLkZlLnByb3RvdHlwZT17CkhSOmZ1bmN0
+aW9uKGEpe2lmKCh0aGlzLmMmMTUpIT09NilyZXR1cm4hMApyZXR1cm4gdGhpcy5iLmIuYnYodC5hbC5h
+KHRoaXMuZCksYS5hLHQueSx0LkspfSwKS3c6ZnVuY3Rpb24oYSl7dmFyIHM9dGhpcy5lLHI9dC56LHE9
+dC5LLHA9YS5hLG89dGhpcy4kdGkuQygiMi8iKSxuPXRoaXMuYi5iCmlmKHQuYWcuYihzKSlyZXR1cm4g
+by5hKG4ucnAocyxwLGEuYixyLHEsdC5sKSkKZWxzZSByZXR1cm4gby5hKG4uYnYodC5iSS5hKHMpLHAs
+cixxKSl9fQpQLnZzLnByb3RvdHlwZT17ClNxOmZ1bmN0aW9uKGEsYixjKXt2YXIgcyxyLHEscD10aGlz
+LiR0aQpwLktxKGMpLkMoIjEvKDIpIikuYShhKQpzPSQuWDMKaWYocyE9PUMuTlUpe2MuQygiQDwwLz4i
+KS5LcShwLmMpLkMoIjEoMikiKS5hKGEpCmlmKGIhPW51bGwpYj1QLlZIKGIscyl9cj1uZXcgUC52cyhz
+LGMuQygidnM8MD4iKSkKcT1iPT1udWxsPzE6Mwp0aGlzLnhmKG5ldyBQLkZlKHIscSxhLGIscC5DKCJA
+PDE+IikuS3EoYykuQygiRmU8MSwyPiIpKSkKcmV0dXJuIHJ9LApXNzpmdW5jdGlvbihhLGIpe3JldHVy
+biB0aGlzLlNxKGEsbnVsbCxiKX0sClFkOmZ1bmN0aW9uKGEsYixjKXt2YXIgcyxyPXRoaXMuJHRpCnIu
+S3EoYykuQygiMS8oMikiKS5hKGEpCnM9bmV3IFAudnMoJC5YMyxjLkMoInZzPDA+IikpCnRoaXMueGYo
+bmV3IFAuRmUocywxOSxhLGIsci5DKCJAPDE+IikuS3EoYykuQygiRmU8MSwyPiIpKSkKcmV0dXJuIHN9
+LAp4ZjpmdW5jdGlvbihhKXt2YXIgcyxyPXRoaXMscT1yLmEKaWYocTw9MSl7YS5hPXQuZS5hKHIuYykK
+ci5jPWF9ZWxzZXtpZihxPT09Mil7cz10LmMuYShyLmMpCnE9cy5hCmlmKHE8NCl7cy54ZihhKQpyZXR1
+cm59ci5hPXEKci5jPXMuY31QLlRrKG51bGwsbnVsbCxyLmIsdC5NLmEobmV3IFAuZGEocixhKSkpfX0s
+CmpROmZ1bmN0aW9uKGEpe3ZhciBzLHIscSxwLG8sbixtPXRoaXMsbD17fQpsLmE9YQppZihhPT1udWxs
+KXJldHVybgpzPW0uYQppZihzPD0xKXtyPXQuZS5hKG0uYykKbS5jPWEKaWYociE9bnVsbCl7cT1hLmEK
+Zm9yKHA9YTtxIT1udWxsO3A9cSxxPW8pbz1xLmEKcC5hPXJ9fWVsc2V7aWYocz09PTIpe249dC5jLmEo
+bS5jKQpzPW4uYQppZihzPDQpe24ualEoYSkKcmV0dXJufW0uYT1zCm0uYz1uLmN9bC5hPW0uTjgoYSkK
+UC5UayhudWxsLG51bGwsbS5iLHQuTS5hKG5ldyBQLm9RKGwsbSkpKX19LAphaDpmdW5jdGlvbigpe3Zh
+ciBzPXQuZS5hKHRoaXMuYykKdGhpcy5jPW51bGwKcmV0dXJuIHRoaXMuTjgocyl9LApOODpmdW5jdGlv
+bihhKXt2YXIgcyxyLHEKZm9yKHM9YSxyPW51bGw7cyE9bnVsbDtyPXMscz1xKXtxPXMuYQpzLmE9cn1y
+ZXR1cm4gcn0sCmVjOmZ1bmN0aW9uKGEpe3ZhciBzLHIscSxwPXRoaXMKcC5hPTEKdHJ5e2EuU3EobmV3
+IFAucFYocCksbmV3IFAuVTcocCksdC5QKX1jYXRjaChxKXtzPUguUnUocSkKcj1ILnRzKHEpClAucmIo
+bmV3IFAudnIocCxzLHIpKX19LApYMjpmdW5jdGlvbihhKXt2YXIgcyxyPXRoaXMKci4kdGkuYy5hKGEp
+CnM9ci5haCgpCnIuYT00CnIuYz1hClAuSFoocixzKX0sClpMOmZ1bmN0aW9uKGEsYil7dmFyIHMscixx
+PXRoaXMKdC5sLmEoYikKcz1xLmFoKCkKcj1QLlRsKGEsYikKcS5hPTgKcS5jPXIKUC5IWihxLHMpfSwK
+WGY6ZnVuY3Rpb24oYSl7dmFyIHM9dGhpcy4kdGkKcy5DKCIxLyIpLmEoYSkKaWYocy5DKCJiODwxPiIp
+LmIoYSkpe3RoaXMuY1UoYSkKcmV0dXJufXRoaXMud1Uocy5jLmEoYSkpfSwKd1U6ZnVuY3Rpb24oYSl7
+dmFyIHM9dGhpcwpzLiR0aS5jLmEoYSkKcy5hPTEKUC5UayhudWxsLG51bGwscy5iLHQuTS5hKG5ldyBQ
+LnJ0KHMsYSkpKX0sCmNVOmZ1bmN0aW9uKGEpe3ZhciBzPXRoaXMscj1zLiR0aQpyLkMoImI4PDE+Iiku
+YShhKQppZihyLmIoYSkpe2lmKGEuYT09PTgpe3MuYT0xClAuVGsobnVsbCxudWxsLHMuYix0Lk0uYShu
+ZXcgUC5LRihzLGEpKSl9ZWxzZSBQLkE5KGEscykKcmV0dXJufXMuZWMoYSl9LApOazpmdW5jdGlvbihh
+LGIpe3RoaXMuYT0xClAuVGsobnVsbCxudWxsLHRoaXMuYix0Lk0uYShuZXcgUC5aTCh0aGlzLGEsYikp
+KX0sCiRpYjg6MX0KUC5kYS5wcm90b3R5cGU9ewokMDpmdW5jdGlvbigpe1AuSFoodGhpcy5hLHRoaXMu
+Yil9LAokUzowfQpQLm9RLnByb3RvdHlwZT17CiQwOmZ1bmN0aW9uKCl7UC5IWih0aGlzLmIsdGhpcy5h
+LmEpfSwKJFM6MH0KUC5wVi5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXt2YXIgcyxyLHEscD10aGlz
+LmEKcC5hPTAKdHJ5e3AuWDIocC4kdGkuYy5hKGEpKX1jYXRjaChxKXtzPUguUnUocSkKcj1ILnRzKHEp
+CnAuWkwocyxyKX19LAokUzoxM30KUC5VNy5wcm90b3R5cGU9ewokMjpmdW5jdGlvbihhLGIpe3RoaXMu
+YS5aTCh0LksuYShhKSx0LmwuYShiKSl9LAokQzoiJDIiLAokUjoyLAokUzozNH0KUC52ci5wcm90b3R5
+cGU9ewokMDpmdW5jdGlvbigpe3RoaXMuYS5aTCh0aGlzLmIsdGhpcy5jKX0sCiRTOjB9ClAucnQucHJv
+dG90eXBlPXsKJDA6ZnVuY3Rpb24oKXt0aGlzLmEuWDIodGhpcy5iKX0sCiRTOjB9ClAuS0YucHJvdG90
+eXBlPXsKJDA6ZnVuY3Rpb24oKXtQLkE5KHRoaXMuYix0aGlzLmEpfSwKJFM6MH0KUC5aTC5wcm90b3R5
+cGU9ewokMDpmdW5jdGlvbigpe3RoaXMuYS5aTCh0aGlzLmIsdGhpcy5jKX0sCiRTOjB9ClAuUlQucHJv
+dG90eXBlPXsKJDA6ZnVuY3Rpb24oKXt2YXIgcyxyLHEscCxvLG4sbT10aGlzLGw9bnVsbAp0cnl7cT1t
+LmEuYQpsPXEuYi5iLnp6KHQuZk8uYShxLmQpLHQueil9Y2F0Y2gocCl7cz1ILlJ1KHApCnI9SC50cyhw
+KQpxPW0uYyYmdC5uLmEobS5iLmEuYykuYT09PXMKbz1tLmEKaWYocSlvLmM9dC5uLmEobS5iLmEuYykK
+ZWxzZSBvLmM9UC5UbChzLHIpCm8uYj0hMApyZXR1cm59aWYobCBpbnN0YW5jZW9mIFAudnMmJmwuYT49
+NCl7aWYobC5hPT09OCl7cT1tLmEKcS5jPXQubi5hKGwuYykKcS5iPSEwfXJldHVybn1pZih0LmkuYihs
+KSl7bj1tLmIuYQpxPW0uYQpxLmM9bC5XNyhuZXcgUC5qWihuKSx0LnopCnEuYj0hMX19LAokUzowfQpQ
+LmpaLnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmF9LAokUzoyOX0KUC5ycS5w
+cm90b3R5cGU9ewokMDpmdW5jdGlvbigpe3ZhciBzLHIscSxwLG8sbixtLGwKdHJ5e3E9dGhpcy5hCnA9
+cS5hCm89cC4kdGkKbj1vLmMKbT1uLmEodGhpcy5iKQpxLmM9cC5iLmIuYnYoby5DKCIyLygxKSIpLmEo
+cC5kKSxtLG8uQygiMi8iKSxuKX1jYXRjaChsKXtzPUguUnUobCkKcj1ILnRzKGwpCnE9dGhpcy5hCnEu
+Yz1QLlRsKHMscikKcS5iPSEwfX0sCiRTOjB9ClAuUlcucHJvdG90eXBlPXsKJDA6ZnVuY3Rpb24oKXt2
+YXIgcyxyLHEscCxvLG4sbT10aGlzCnRyeXtzPXQubi5hKG0uYS5hLmMpCnA9bS5iCmlmKHAuYS5IUihz
+KSYmcC5hLmUhPW51bGwpe3AuYz1wLmEuS3cocykKcC5iPSExfX1jYXRjaChvKXtyPUguUnUobykKcT1I
+LnRzKG8pCnA9dC5uLmEobS5hLmEuYykKbj1tLmIKaWYocC5hPT09ciluLmM9cAplbHNlIG4uYz1QLlRs
+KHIscSkKbi5iPSEwfX0sCiRTOjB9ClAuT00ucHJvdG90eXBlPXt9ClAucWgucHJvdG90eXBlPXsKZ0E6
+ZnVuY3Rpb24oYSl7dmFyIHMscixxPXRoaXMscD17fSxvPW5ldyBQLnZzKCQuWDMsdC5mSikKcC5hPTAK
+cz1ILkxoKHEpCnI9cy5DKCJ+KDEpPyIpLmEobmV3IFAuQjUocCxxKSkKdC5aLmEobmV3IFAudU8ocCxv
+KSkKVy5KRShxLmEscS5iLHIsITEscy5jKQpyZXR1cm4gb319ClAuQjUucHJvdG90eXBlPXsKJDE6ZnVu
+Y3Rpb24oYSl7SC5MaCh0aGlzLmIpLmMuYShhKTsrK3RoaXMuYS5hfSwKJFM6ZnVuY3Rpb24oKXtyZXR1
+cm4gSC5MaCh0aGlzLmIpLkMoIn4oMSkiKX19ClAudU8ucHJvdG90eXBlPXsKJDA6ZnVuY3Rpb24oKXt2
+YXIgcz10aGlzLmIscj1zLiR0aSxxPXIuQygiMS8iKS5hKHRoaXMuYS5hKSxwPXMuYWgoKQpyLmMuYShx
+KQpzLmE9NApzLmM9cQpQLkhaKHMscCl9LAokUzowfQpQLk1PLnByb3RvdHlwZT17fQpQLmtULnByb3Rv
+dHlwZT17fQpQLnhJLnByb3RvdHlwZT17fQpQLm0wLnByb3RvdHlwZT17JGlRbToxfQpQLkV2LnByb3Rv
+dHlwZT17CiQwOmZ1bmN0aW9uKCl7dmFyIHM9dC5LLmEoSC5iKHRoaXMuYSkpCnMuc3RhY2s9dGhpcy5i
+LncoMCkKdGhyb3cgc30sCiRTOjB9ClAuSmkucHJvdG90eXBlPXsKYkg6ZnVuY3Rpb24oYSl7dmFyIHMs
+cixxLHAsbwp0Lk0uYShhKQp0cnl7aWYoQy5OVT09PSQuWDMpe2EuJDAoKQpyZXR1cm59UC5UOChudWxs
+LG51bGwsdGhpcyxhLHQuSCl9Y2F0Y2gocSl7cz1ILlJ1KHEpCnI9SC50cyhxKQpwPXQuSy5hKHMpCm89
+dC5sLmEocikKUC5TaShwLG8pfX0sCkRsOmZ1bmN0aW9uKGEsYixjKXt2YXIgcyxyLHEscCxvCmMuQygi
+figwKSIpLmEoYSkKYy5hKGIpCnRyeXtpZihDLk5VPT09JC5YMyl7YS4kMShiKQpyZXR1cm59UC55dihu
+dWxsLG51bGwsdGhpcyxhLGIsdC5ILGMpfWNhdGNoKHEpe3M9SC5SdShxKQpyPUgudHMocSkKcD10Lksu
+YShzKQpvPXQubC5hKHIpClAuU2kocCxvKX19LApHWTpmdW5jdGlvbihhKXtyZXR1cm4gbmV3IFAuVnAo
+dGhpcyx0Lk0uYShhKSl9LApQeTpmdW5jdGlvbihhLGIpe3JldHVybiBuZXcgUC5PUih0aGlzLGIuQygi
+figwKSIpLmEoYSksYil9LApxOmZ1bmN0aW9uKGEsYil7cmV0dXJuIG51bGx9LAp6ejpmdW5jdGlvbihh
+LGIpe2IuQygiMCgpIikuYShhKQppZigkLlgzPT09Qy5OVSlyZXR1cm4gYS4kMCgpCnJldHVybiBQLlQ4
+KG51bGwsbnVsbCx0aGlzLGEsYil9LApidjpmdW5jdGlvbihhLGIsYyxkKXtjLkMoIkA8MD4iKS5LcShk
+KS5DKCIxKDIpIikuYShhKQpkLmEoYikKaWYoJC5YMz09PUMuTlUpcmV0dXJuIGEuJDEoYikKcmV0dXJu
+IFAueXYobnVsbCxudWxsLHRoaXMsYSxiLGMsZCl9LApycDpmdW5jdGlvbihhLGIsYyxkLGUsZil7ZC5D
+KCJAPDA+IikuS3EoZSkuS3EoZikuQygiMSgyLDMpIikuYShhKQplLmEoYikKZi5hKGMpCmlmKCQuWDM9
+PT1DLk5VKXJldHVybiBhLiQyKGIsYykKcmV0dXJuIFAuUXgobnVsbCxudWxsLHRoaXMsYSxiLGMsZCxl
+LGYpfSwKTGo6ZnVuY3Rpb24oYSxiLGMsZCl7cmV0dXJuIGIuQygiQDwwPiIpLktxKGMpLktxKGQpLkMo
+IjEoMiwzKSIpLmEoYSl9fQpQLlZwLnByb3RvdHlwZT17CiQwOmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMu
+YS5iSCh0aGlzLmIpfSwKJFM6MH0KUC5PUi5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXt2YXIgcz10
+aGlzLmMKcmV0dXJuIHRoaXMuYS5EbCh0aGlzLmIscy5hKGEpLHMpfSwKJFM6ZnVuY3Rpb24oKXtyZXR1
+cm4gdGhpcy5jLkMoIn4oMCkiKX19ClAuYjYucHJvdG90eXBlPXsKZ206ZnVuY3Rpb24oYSl7dmFyIHM9
+dGhpcyxyPW5ldyBQLmxtKHMscy5yLEguTGgocykuQygibG08MT4iKSkKci5jPXMuZQpyZXR1cm4gcn0s
+CmdBOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmF9LApnbDA6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMu
+YT09PTB9LApnb3I6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuYSE9PTB9LAp0ZzpmdW5jdGlvbihhLGIp
+e3ZhciBzLHIKaWYoYiE9PSJfX3Byb3RvX18iKXtzPXRoaXMuYgppZihzPT1udWxsKXJldHVybiExCnJl
+dHVybiB0LmcuYShzW2JdKSE9bnVsbH1lbHNle3I9dGhpcy5QUihiKQpyZXR1cm4gcn19LApQUjpmdW5j
+dGlvbihhKXt2YXIgcz10aGlzLmQKaWYocz09bnVsbClyZXR1cm4hMQpyZXR1cm4gdGhpcy5ERihzW3Ro
+aXMuTihhKV0sYSk+PTB9LAppOmZ1bmN0aW9uKGEsYil7dmFyIHMscixxPXRoaXMKSC5MaChxKS5jLmEo
+YikKaWYodHlwZW9mIGI9PSJzdHJpbmciJiZiIT09Il9fcHJvdG9fXyIpe3M9cS5iCnJldHVybiBxLkoo
+cz09bnVsbD9xLmI9UC5UMigpOnMsYil9ZWxzZSBpZih0eXBlb2YgYj09Im51bWJlciImJihiJjEwNzM3
+NDE4MjMpPT09Yil7cj1xLmMKcmV0dXJuIHEuSihyPT1udWxsP3EuYz1QLlQyKCk6cixiKX1lbHNlIHJl
+dHVybiBxLlkoYil9LApZOmZ1bmN0aW9uKGEpe3ZhciBzLHIscSxwPXRoaXMKSC5MaChwKS5jLmEoYSkK
+cz1wLmQKaWYocz09bnVsbClzPXAuZD1QLlQyKCkKcj1wLk4oYSkKcT1zW3JdCmlmKHE9PW51bGwpc1ty
+XT1bcC55byhhKV0KZWxzZXtpZihwLkRGKHEsYSk+PTApcmV0dXJuITEKcS5wdXNoKHAueW8oYSkpfXJl
+dHVybiEwfSwKUjpmdW5jdGlvbihhLGIpe3ZhciBzPXRoaXMKaWYodHlwZW9mIGI9PSJzdHJpbmciJiZi
+IT09Il9fcHJvdG9fXyIpcmV0dXJuIHMuSChzLmIsYikKZWxzZSBpZih0eXBlb2YgYj09Im51bWJlciIm
+JihiJjEwNzM3NDE4MjMpPT09YilyZXR1cm4gcy5IKHMuYyxiKQplbHNlIHJldHVybiBzLnFnKGIpfSwK
+cWc6ZnVuY3Rpb24oYSl7dmFyIHMscixxLHAsbz10aGlzLG49by5kCmlmKG49PW51bGwpcmV0dXJuITEK
+cz1vLk4oYSkKcj1uW3NdCnE9by5ERihyLGEpCmlmKHE8MClyZXR1cm4hMQpwPXIuc3BsaWNlKHEsMSlb
+MF0KaWYoMD09PXIubGVuZ3RoKWRlbGV0ZSBuW3NdCm8uRyhwKQpyZXR1cm4hMH0sCko6ZnVuY3Rpb24o
+YSxiKXtILkxoKHRoaXMpLmMuYShiKQppZih0LmcuYShhW2JdKSE9bnVsbClyZXR1cm4hMQphW2JdPXRo
+aXMueW8oYikKcmV0dXJuITB9LApIOmZ1bmN0aW9uKGEsYil7dmFyIHMKaWYoYT09bnVsbClyZXR1cm4h
+MQpzPXQuZy5hKGFbYl0pCmlmKHM9PW51bGwpcmV0dXJuITEKdGhpcy5HKHMpCmRlbGV0ZSBhW2JdCnJl
+dHVybiEwfSwKUzpmdW5jdGlvbigpe3RoaXMucj10aGlzLnIrMSYxMDczNzQxODIzfSwKeW86ZnVuY3Rp
+b24oYSl7dmFyIHMscj10aGlzLHE9bmV3IFAuYm4oSC5MaChyKS5jLmEoYSkpCmlmKHIuZT09bnVsbCly
+LmU9ci5mPXEKZWxzZXtzPXIuZgpzLnRvU3RyaW5nCnEuYz1zCnIuZj1zLmI9cX0rK3IuYQpyLlMoKQpy
+ZXR1cm4gcX0sCkc6ZnVuY3Rpb24oYSl7dmFyIHM9dGhpcyxyPWEuYyxxPWEuYgppZihyPT1udWxsKXMu
+ZT1xCmVsc2Ugci5iPXEKaWYocT09bnVsbClzLmY9cgplbHNlIHEuYz1yOy0tcy5hCnMuUygpfSwKTjpm
+dW5jdGlvbihhKXtyZXR1cm4gSi5oZihhKSYxMDczNzQxODIzfSwKREY6ZnVuY3Rpb24oYSxiKXt2YXIg
+cyxyCmlmKGE9PW51bGwpcmV0dXJuLTEKcz1hLmxlbmd0aApmb3Iocj0wO3I8czsrK3IpaWYoSi5STShh
+W3JdLmEsYikpcmV0dXJuIHIKcmV0dXJuLTF9fQpQLmJuLnByb3RvdHlwZT17fQpQLmxtLnByb3RvdHlw
+ZT17CmdsOmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuJHRpLmMuYSh0aGlzLmQpfSwKRjpmdW5jdGlvbigp
+e3ZhciBzPXRoaXMscj1zLmMscT1zLmEKaWYocy5iIT09cS5yKXRocm93IEguYihQLmE0KHEpKQplbHNl
+IGlmKHI9PW51bGwpe3Muc2oobnVsbCkKcmV0dXJuITF9ZWxzZXtzLnNqKHMuJHRpLkMoIjE/IikuYShy
+LmEpKQpzLmM9ci5iCnJldHVybiEwfX0sCnNqOmZ1bmN0aW9uKGEpe3RoaXMuZD10aGlzLiR0aS5DKCIx
+PyIpLmEoYSl9LAokaUFuOjF9ClAubVcucHJvdG90eXBlPXt9ClAudXkucHJvdG90eXBlPXskaWJROjEs
+JGljWDoxLCRpek06MX0KUC5sRC5wcm90b3R5cGU9ewpnbTpmdW5jdGlvbihhKXtyZXR1cm4gbmV3IEgu
+YTcoYSx0aGlzLmdBKGEpLEgueksoYSkuQygiYTc8bEQuRT4iKSl9LApFOmZ1bmN0aW9uKGEsYil7cmV0
+dXJuIHRoaXMucShhLGIpfSwKSzpmdW5jdGlvbihhLGIpe3ZhciBzLHIKSC56SyhhKS5DKCJ+KGxELkUp
+IikuYShiKQpzPXRoaXMuZ0EoYSkKZm9yKHI9MDtyPHM7KytyKXtiLiQxKHRoaXMucShhLHIpKQppZihz
+IT09dGhpcy5nQShhKSl0aHJvdyBILmIoUC5hNChhKSl9fSwKZ2wwOmZ1bmN0aW9uKGEpe3JldHVybiB0
+aGlzLmdBKGEpPT09MH0sCmdvcjpmdW5jdGlvbihhKXtyZXR1cm4hdGhpcy5nbDAoYSl9LApFMjpmdW5j
+dGlvbihhLGIsYyl7dmFyIHM9SC56SyhhKQpyZXR1cm4gbmV3IEgubEooYSxzLktxKGMpLkMoIjEobEQu
+RSkiKS5hKGIpLHMuQygiQDxsRC5FPiIpLktxKGMpLkMoImxKPDEsMj4iKSl9LAplUjpmdW5jdGlvbihh
+LGIpe3JldHVybiBILnFDKGEsYixudWxsLEgueksoYSkuQygibEQuRSIpKX0sCmRyOmZ1bmN0aW9uKGEs
+Yil7cmV0dXJuIG5ldyBILmpWKGEsSC56SyhhKS5DKCJAPGxELkU+IikuS3EoYikuQygialY8MSwyPiIp
+KX0sCmR1OmZ1bmN0aW9uKGEsYixjLGQpe3ZhciBzLHI9SC56SyhhKQpkPXIuQygibEQuRSIpLmEoci5D
+KCJsRC5FPyIpLmEoZCkpClAuakIoYixjLHRoaXMuZ0EoYSkpCmZvcihzPWI7czxjOysrcyl0aGlzLlk1
+KGEscyxkKX0sCnc6ZnVuY3Rpb24oYSl7cmV0dXJuIFAueChhLCJbIiwiXSIpfX0KUC5pbC5wcm90b3R5
+cGU9e30KUC5yYS5wcm90b3R5cGU9ewokMjpmdW5jdGlvbihhLGIpe3ZhciBzLHI9dGhpcy5hCmlmKCFy
+LmEpdGhpcy5iLmErPSIsICIKci5hPSExCnI9dGhpcy5iCnM9ci5hKz1ILkVqKGEpCnIuYT1zKyI6ICIK
+ci5hKz1ILkVqKGIpfSwKJFM6OX0KUC5Zay5wcm90b3R5cGU9ewpLOmZ1bmN0aW9uKGEsYil7dmFyIHMs
+cixxPUguTGgodGhpcykKcS5DKCJ+KFlrLkssWWsuVikiKS5hKGIpCmZvcihzPUouSVQodGhpcy5ndmMo
+KSkscT1xLkMoIllrLlYiKTtzLkYoKTspe3I9cy5nbCgpCmIuJDIocixxLmEodGhpcy5xKDAscikpKX19
+LApnUHU6ZnVuY3Rpb24oYSl7cmV0dXJuIEouTTEodGhpcy5ndmMoKSxuZXcgUC55USh0aGlzKSxILkxo
+KHRoaXMpLkMoIk4zPFlrLkssWWsuVj4iKSl9LAp4NDpmdW5jdGlvbihhKXtyZXR1cm4gSi56bCh0aGlz
+Lmd2YygpLGEpfSwKZ0E6ZnVuY3Rpb24oYSl7cmV0dXJuIEouSG0odGhpcy5ndmMoKSl9LApnbDA6ZnVu
+Y3Rpb24oYSl7cmV0dXJuIEoudVUodGhpcy5ndmMoKSl9LAp3OmZ1bmN0aW9uKGEpe3JldHVybiBQLm5P
+KHRoaXMpfSwKJGlaMDoxfQpQLnlRLnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3ZhciBzLHI9dGhp
+cy5hLHE9SC5MaChyKQpxLkMoIllrLksiKS5hKGEpCnM9cS5DKCJZay5WIikKcmV0dXJuIG5ldyBQLk4z
+KGEscy5hKHIucSgwLGEpKSxxLkMoIkA8WWsuSz4iKS5LcShzKS5DKCJOMzwxLDI+IikpfSwKJFM6ZnVu
+Y3Rpb24oKXtyZXR1cm4gSC5MaCh0aGlzLmEpLkMoIk4zPFlrLkssWWsuVj4oWWsuSykiKX19ClAuS1Au
+cHJvdG90eXBlPXsKWTU6ZnVuY3Rpb24oYSxiLGMpe3ZhciBzPUguTGgodGhpcykKcy5jLmEoYikKcy5R
+WzFdLmEoYykKdGhyb3cgSC5iKFAuTDQoIkNhbm5vdCBtb2RpZnkgdW5tb2RpZmlhYmxlIG1hcCIpKX19
+ClAuUG4ucHJvdG90eXBlPXsKcTpmdW5jdGlvbihhLGIpe3JldHVybiB0aGlzLmEucSgwLGIpfSwKWTU6
+ZnVuY3Rpb24oYSxiLGMpe3ZhciBzPUguTGgodGhpcykKdGhpcy5hLlk1KDAscy5jLmEoYikscy5RWzFd
+LmEoYykpfSwKeDQ6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuYS54NChhKX0sCks6ZnVuY3Rpb24oYSxi
+KXt0aGlzLmEuSygwLEguTGgodGhpcykuQygifigxLDIpIikuYShiKSl9LApnbDA6ZnVuY3Rpb24oYSl7
+dmFyIHM9dGhpcy5hCnJldHVybiBzLmdsMChzKX0sCmdBOmZ1bmN0aW9uKGEpe3ZhciBzPXRoaXMuYQpy
+ZXR1cm4gcy5nQShzKX0sCnc6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuYS53KDApfSwKZ1B1OmZ1bmN0
+aW9uKGEpe3ZhciBzPXRoaXMuYQpyZXR1cm4gcy5nUHUocyl9LAokaVowOjF9ClAuR2oucHJvdG90eXBl
+PXt9ClAubGYucHJvdG90eXBlPXsKZ2wwOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmdBKHRoaXMpPT09
+MH0sCmdvcjpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5nQSh0aGlzKSE9PTB9LApGVjpmdW5jdGlvbihh
+LGIpe3ZhciBzCmZvcihzPUouSVQoSC5MaCh0aGlzKS5DKCJjWDxsZi5FPiIpLmEoYikpO3MuRigpOyl0
+aGlzLmkoMCxzLmdsKCkpfSwKdzpmdW5jdGlvbihhKXtyZXR1cm4gUC54KHRoaXMsInsiLCJ9Iil9LApr
+OmZ1bmN0aW9uKGEsYil7dmFyIHMscixxPXRoaXMuZ20odGhpcykKaWYoIXEuRigpKXJldHVybiIiCnM9
+cS4kdGkuYwppZihiPT09IiIpe3I9IiIKZG8gcis9SC5FaihzLmEocS5kKSkKd2hpbGUocS5GKCkpCnM9
+cn1lbHNle3I9IiIrSC5FaihzLmEocS5kKSkKZm9yKDtxLkYoKTspcj1yK2IrSC5FaihzLmEocS5kKSkK
+cz1yfXJldHVybiBzLmNoYXJDb2RlQXQoMCk9PTA/czpzfSwKZVI6ZnVuY3Rpb24oYSxiKXtyZXR1cm4g
+SC5iSyh0aGlzLGIsSC5MaCh0aGlzKS5DKCJsZi5FIikpfSwKRTpmdW5jdGlvbihhLGIpe3ZhciBzLHIs
+cSxwLG89ImluZGV4IgpILmNiKGIsbyx0LlMpClAuazEoYixvKQpmb3Iocz10aGlzLmdtKHRoaXMpLHI9
+cy4kdGkuYyxxPTA7cy5GKCk7KXtwPXIuYShzLmQpCmlmKGI9PT1xKXJldHVybiBwOysrcX10aHJvdyBI
+LmIoUC5DZihiLHRoaXMsbyxudWxsLHEpKX19ClAuVmoucHJvdG90eXBlPXskaWJROjEsJGljWDoxLCRp
+eHU6MX0KUC5Ydi5wcm90b3R5cGU9eyRpYlE6MSwkaWNYOjEsJGl4dToxfQpQLm5ZLnByb3RvdHlwZT17
+fQpQLldZLnByb3RvdHlwZT17fQpQLlJVLnByb3RvdHlwZT17fQpQLnBSLnByb3RvdHlwZT17fQpQLnV3
+LnByb3RvdHlwZT17CnE6ZnVuY3Rpb24oYSxiKXt2YXIgcyxyPXRoaXMuYgppZihyPT1udWxsKXJldHVy
+biB0aGlzLmMucSgwLGIpCmVsc2UgaWYodHlwZW9mIGIhPSJzdHJpbmciKXJldHVybiBudWxsCmVsc2V7
+cz1yW2JdCnJldHVybiB0eXBlb2Ygcz09InVuZGVmaW5lZCI/dGhpcy5mYihiKTpzfX0sCmdBOmZ1bmN0
+aW9uKGEpe3JldHVybiB0aGlzLmI9PW51bGw/dGhpcy5jLmE6dGhpcy5DZigpLmxlbmd0aH0sCmdsMDpm
+dW5jdGlvbihhKXtyZXR1cm4gdGhpcy5nQSh0aGlzKT09PTB9LApndmM6ZnVuY3Rpb24oKXtpZih0aGlz
+LmI9PW51bGwpe3ZhciBzPXRoaXMuYwpyZXR1cm4gbmV3IEguaTUocyxILkxoKHMpLkMoImk1PDE+Iikp
+fXJldHVybiBuZXcgUC5pOCh0aGlzKX0sClk1OmZ1bmN0aW9uKGEsYixjKXt2YXIgcyxyLHE9dGhpcwpp
+ZihxLmI9PW51bGwpcS5jLlk1KDAsYixjKQplbHNlIGlmKHEueDQoYikpe3M9cS5iCnNbYl09YwpyPXEu
+YQppZihyPT1udWxsP3MhPW51bGw6ciE9PXMpcltiXT1udWxsfWVsc2UgcS5YSygpLlk1KDAsYixjKX0s
+Cng0OmZ1bmN0aW9uKGEpe2lmKHRoaXMuYj09bnVsbClyZXR1cm4gdGhpcy5jLng0KGEpCnJldHVybiBP
+YmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwodGhpcy5hLGEpfSwKSzpmdW5jdGlvbihh
+LGIpe3ZhciBzLHIscSxwLG89dGhpcwp0LmNBLmEoYikKaWYoby5iPT1udWxsKXJldHVybiBvLmMuSygw
+LGIpCnM9by5DZigpCmZvcihyPTA7cjxzLmxlbmd0aDsrK3Ipe3E9c1tyXQpwPW8uYltxXQppZih0eXBl
+b2YgcD09InVuZGVmaW5lZCIpe3A9UC5RZShvLmFbcV0pCm8uYltxXT1wfWIuJDIocSxwKQppZihzIT09
+by5jKXRocm93IEguYihQLmE0KG8pKX19LApDZjpmdW5jdGlvbigpe3ZhciBzPXQuYk0uYSh0aGlzLmMp
+CmlmKHM9PW51bGwpcz10aGlzLmM9SC5RSShPYmplY3Qua2V5cyh0aGlzLmEpLHQucykKcmV0dXJuIHN9
+LApYSzpmdW5jdGlvbigpe3ZhciBzLHIscSxwLG8sbj10aGlzCmlmKG4uYj09bnVsbClyZXR1cm4gbi5j
+CnM9UC5GbCh0Lk4sdC56KQpyPW4uQ2YoKQpmb3IocT0wO3A9ci5sZW5ndGgscTxwOysrcSl7bz1yW3Fd
+CnMuWTUoMCxvLG4ucSgwLG8pKX1pZihwPT09MClDLk5tLmkociwiIikKZWxzZSBDLk5tLnNBKHIsMCkK
+bi5hPW4uYj1udWxsCnJldHVybiBuLmM9c30sCmZiOmZ1bmN0aW9uKGEpe3ZhciBzCmlmKCFPYmplY3Qu
+cHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwodGhpcy5hLGEpKXJldHVybiBudWxsCnM9UC5RZSh0
+aGlzLmFbYV0pCnJldHVybiB0aGlzLmJbYV09c319ClAuaTgucHJvdG90eXBlPXsKZ0E6ZnVuY3Rpb24o
+YSl7dmFyIHM9dGhpcy5hCnJldHVybiBzLmdBKHMpfSwKRTpmdW5jdGlvbihhLGIpe3ZhciBzPXRoaXMu
+YQppZihzLmI9PW51bGwpcz1zLmd2YygpLkUoMCxiKQplbHNle3M9cy5DZigpCmlmKGI8MHx8Yj49cy5s
+ZW5ndGgpcmV0dXJuIEguT0gocyxiKQpzPXNbYl19cmV0dXJuIHN9LApnbTpmdW5jdGlvbihhKXt2YXIg
+cz10aGlzLmEKaWYocy5iPT1udWxsKXtzPXMuZ3ZjKCkKcz1zLmdtKHMpfWVsc2V7cz1zLkNmKCkKcz1u
+ZXcgSi5tMShzLHMubGVuZ3RoLEgudDYocykuQygibTE8MT4iKSl9cmV0dXJuIHN9LAp0ZzpmdW5jdGlv
+bihhLGIpe3JldHVybiB0aGlzLmEueDQoYil9fQpQLnhyLnByb3RvdHlwZT17CiQwOmZ1bmN0aW9uKCl7
+dmFyIHMscgp0cnl7cz1uZXcgVGV4dERlY29kZXIoInV0Zi04Iix7ZmF0YWw6dHJ1ZX0pCnJldHVybiBz
+fWNhdGNoKHIpe0guUnUocil9cmV0dXJuIG51bGx9LAokUzoxMH0KUC5Oei5wcm90b3R5cGU9ewokMDpm
+dW5jdGlvbigpe3ZhciBzLHIKdHJ5e3M9bmV3IFRleHREZWNvZGVyKCJ1dGYtOCIse2ZhdGFsOmZhbHNl
+fSkKcmV0dXJuIHN9Y2F0Y2gocil7SC5SdShyKX1yZXR1cm4gbnVsbH0sCiRTOjEwfQpQLkNWLnByb3Rv
+dHlwZT17CnlyOmZ1bmN0aW9uKGExLGEyLGEzKXt2YXIgcyxyLHEscCxvLG4sbSxsLGssaixpLGgsZyxm
+LGUsZCxjLGIsYSxhMD0iSW52YWxpZCBiYXNlNjQgZW5jb2RpbmcgbGVuZ3RoICIKYTM9UC5qQihhMixh
+MyxhMS5sZW5ndGgpCnM9JC5WNygpCmZvcihyPXMubGVuZ3RoLHE9YTIscD1xLG89bnVsbCxuPS0xLG09
+LTEsbD0wO3E8YTM7cT1rKXtrPXErMQpqPUMueEIuVyhhMSxxKQppZihqPT09Mzcpe2k9aysyCmlmKGk8
+PWEzKXtoPUgub28oQy54Qi5XKGExLGspKQpnPUgub28oQy54Qi5XKGExLGsrMSkpCmY9aCoxNitnLShn
+JjI1NikKaWYoZj09PTM3KWY9LTEKaz1pfWVsc2UgZj0tMX1lbHNlIGY9agppZigwPD1mJiZmPD0xMjcp
+e2lmKGY8MHx8Zj49cilyZXR1cm4gSC5PSChzLGYpCmU9c1tmXQppZihlPj0wKXtmPUMueEIuTygiQUJD
+REVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0NTY3ODkr
+LyIsZSkKaWYoZj09PWopY29udGludWUKaj1mfWVsc2V7aWYoZT09PS0xKXtpZihuPDApe2Q9bz09bnVs
+bD9udWxsOm8uYS5sZW5ndGgKaWYoZD09bnVsbClkPTAKbj1kKyhxLXApCm09cX0rK2wKaWYoaj09PTYx
+KWNvbnRpbnVlfWo9Zn1pZihlIT09LTIpe2lmKG89PW51bGwpe289bmV3IFAuTSgiIikKZD1vfWVsc2Ug
+ZD1vCmM9ZC5hKz1DLnhCLk5qKGExLHAscSkKZC5hPWMrSC5MdyhqKQpwPWsKY29udGludWV9fXRocm93
+IEguYihQLnJyKCJJbnZhbGlkIGJhc2U2NCBkYXRhIixhMSxxKSl9aWYobyE9bnVsbCl7cj1vLmErPUMu
+eEIuTmooYTEscCxhMykKZD1yLmxlbmd0aAppZihuPj0wKVAueE0oYTEsbSxhMyxuLGwsZCkKZWxzZXti
+PUMuam4uelkoZC0xLDQpKzEKaWYoYj09PTEpdGhyb3cgSC5iKFAucnIoYTAsYTEsYTMpKQpmb3IoO2I8
+NDspe3IrPSI9IgpvLmE9cjsrK2J9fXI9by5hCnJldHVybiBDLnhCLmk3KGExLGEyLGEzLHIuY2hhckNv
+ZGVBdCgwKT09MD9yOnIpfWE9YTMtYTIKaWYobj49MClQLnhNKGExLG0sYTMsbixsLGEpCmVsc2V7Yj1D
+LmpuLnpZKGEsNCkKaWYoYj09PTEpdGhyb3cgSC5iKFAucnIoYTAsYTEsYTMpKQppZihiPjEpYTE9Qy54
+Qi5pNyhhMSxhMyxhMyxiPT09Mj8iPT0iOiI9Iil9cmV0dXJuIGExfX0KUC5VOC5wcm90b3R5cGU9e30K
+UC5Vay5wcm90b3R5cGU9e30KUC53SS5wcm90b3R5cGU9e30KUC5aaS5wcm90b3R5cGU9e30KUC5VZC5w
+cm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEpe3ZhciBzPVAuaGwodGhpcy5hKQpyZXR1cm4odGhpcy5iIT1u
+dWxsPyJDb252ZXJ0aW5nIG9iamVjdCB0byBhbiBlbmNvZGFibGUgb2JqZWN0IGZhaWxlZDoiOiJDb252
+ZXJ0aW5nIG9iamVjdCBkaWQgbm90IHJldHVybiBhbiBlbmNvZGFibGUgb2JqZWN0OiIpKyIgIitzfX0K
+UC5LOC5wcm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEpe3JldHVybiJDeWNsaWMgZXJyb3IgaW4gSlNPTiBz
+dHJpbmdpZnkifX0KUC5ieS5wcm90b3R5cGU9ewpwVzpmdW5jdGlvbihhLGIsYyl7dmFyIHMKdC5mVi5h
+KGMpCnM9UC5CUyhiLHRoaXMuZ0hlKCkuYSkKcmV0dXJuIHN9LApPQjpmdW5jdGlvbihhLGIpe3ZhciBz
+CnQuZEEuYShiKQpzPVAudVgoYSx0aGlzLmdaRSgpLmIsbnVsbCkKcmV0dXJuIHN9LApnWkU6ZnVuY3Rp
+b24oKXtyZXR1cm4gQy5uWH0sCmdIZTpmdW5jdGlvbigpe3JldHVybiBDLkEzfX0KUC5vai5wcm90b3R5
+cGU9e30KUC5NeC5wcm90b3R5cGU9e30KUC5TaC5wcm90b3R5cGU9ewpSVDpmdW5jdGlvbihhKXt2YXIg
+cyxyLHEscCxvLG4sbT1hLmxlbmd0aApmb3Iocz10aGlzLmMscj0wLHE9MDtxPG07KytxKXtwPUMueEIu
+VyhhLHEpCmlmKHA+OTIpe2lmKHA+PTU1Mjk2KXtvPXAmNjQ1MTIKaWYobz09PTU1Mjk2KXtuPXErMQpu
+PSEobjxtJiYoQy54Qi5XKGEsbikmNjQ1MTIpPT09NTYzMjApfWVsc2Ugbj0hMQppZighbilpZihvPT09
+NTYzMjApe289cS0xCm89IShvPj0wJiYoQy54Qi5PKGEsbykmNjQ1MTIpPT09NTUyOTYpfWVsc2Ugbz0h
+MQplbHNlIG89ITAKaWYobyl7aWYocT5yKXMuYSs9Qy54Qi5OaihhLHIscSkKcj1xKzEKbz1zLmErPUgu
+THcoOTIpCm8rPUguTHcoMTE3KQpzLmE9bwpvKz1ILkx3KDEwMCkKcy5hPW8Kbj1wPj4+OCYxNQpvKz1I
+Lkx3KG48MTA/NDgrbjo4NytuKQpzLmE9bwpuPXA+Pj40JjE1Cm8rPUguTHcobjwxMD80OCtuOjg3K24p
+CnMuYT1vCm49cCYxNQpzLmE9bytILkx3KG48MTA/NDgrbjo4NytuKX19Y29udGludWV9aWYocDwzMil7
+aWYocT5yKXMuYSs9Qy54Qi5OaihhLHIscSkKcj1xKzEKbz1zLmErPUguTHcoOTIpCnN3aXRjaChwKXtj
+YXNlIDg6cy5hPW8rSC5Mdyg5OCkKYnJlYWsKY2FzZSA5OnMuYT1vK0guTHcoMTE2KQpicmVhawpjYXNl
+IDEwOnMuYT1vK0guTHcoMTEwKQpicmVhawpjYXNlIDEyOnMuYT1vK0guTHcoMTAyKQpicmVhawpjYXNl
+IDEzOnMuYT1vK0guTHcoMTE0KQpicmVhawpkZWZhdWx0Om8rPUguTHcoMTE3KQpzLmE9bwpvKz1ILkx3
+KDQ4KQpzLmE9bwpvKz1ILkx3KDQ4KQpzLmE9bwpuPXA+Pj40JjE1Cm8rPUguTHcobjwxMD80OCtuOjg3
+K24pCnMuYT1vCm49cCYxNQpzLmE9bytILkx3KG48MTA/NDgrbjo4NytuKQpicmVha319ZWxzZSBpZihw
+PT09MzR8fHA9PT05Mil7aWYocT5yKXMuYSs9Qy54Qi5OaihhLHIscSkKcj1xKzEKbz1zLmErPUguTHco
+OTIpCnMuYT1vK0guTHcocCl9fWlmKHI9PT0wKXMuYSs9YQplbHNlIGlmKHI8bSlzLmErPUMueEIuTmoo
+YSxyLG0pfSwKSm46ZnVuY3Rpb24oYSl7dmFyIHMscixxLHAKZm9yKHM9dGhpcy5hLHI9cy5sZW5ndGgs
+cT0wO3E8cjsrK3Epe3A9c1txXQppZihhPT1udWxsP3A9PW51bGw6YT09PXApdGhyb3cgSC5iKG5ldyBQ
+Lks4KGEsbnVsbCkpfUMuTm0uaShzLGEpfSwKaVU6ZnVuY3Rpb24oYSl7dmFyIHMscixxLHAsbz10aGlz
+CmlmKG8udE0oYSkpcmV0dXJuCm8uSm4oYSkKdHJ5e3M9by5iLiQxKGEpCmlmKCFvLnRNKHMpKXtxPVAu
+R3koYSxudWxsLG8uZ1ZLKCkpCnRocm93IEguYihxKX1xPW8uYQppZigwPj1xLmxlbmd0aClyZXR1cm4g
+SC5PSChxLC0xKQpxLnBvcCgpfWNhdGNoKHApe3I9SC5SdShwKQpxPVAuR3koYSxyLG8uZ1ZLKCkpCnRo
+cm93IEguYihxKX19LAp0TTpmdW5jdGlvbihhKXt2YXIgcyxyLHE9dGhpcwppZih0eXBlb2YgYT09Im51
+bWJlciIpe2lmKCFpc0Zpbml0ZShhKSlyZXR1cm4hMQpxLmMuYSs9Qy5DRC53KGEpCnJldHVybiEwfWVs
+c2UgaWYoYT09PSEwKXtxLmMuYSs9InRydWUiCnJldHVybiEwfWVsc2UgaWYoYT09PSExKXtxLmMuYSs9
+ImZhbHNlIgpyZXR1cm4hMH1lbHNlIGlmKGE9PW51bGwpe3EuYy5hKz0ibnVsbCIKcmV0dXJuITB9ZWxz
+ZSBpZih0eXBlb2YgYT09InN0cmluZyIpe3M9cS5jCnMuYSs9JyInCnEuUlQoYSkKcy5hKz0nIicKcmV0
+dXJuITB9ZWxzZSBpZih0LmouYihhKSl7cS5KbihhKQpxLmxLKGEpCnM9cS5hCmlmKDA+PXMubGVuZ3Ro
+KXJldHVybiBILk9IKHMsLTEpCnMucG9wKCkKcmV0dXJuITB9ZWxzZSBpZih0LmYuYihhKSl7cS5Kbihh
+KQpyPXEuancoYSkKcz1xLmEKaWYoMD49cy5sZW5ndGgpcmV0dXJuIEguT0gocywtMSkKcy5wb3AoKQpy
+ZXR1cm4gcn1lbHNlIHJldHVybiExfSwKbEs6ZnVuY3Rpb24oYSl7dmFyIHMscixxPXRoaXMuYwpxLmEr
+PSJbIgpzPUouVTYoYSkKaWYocy5nb3IoYSkpe3RoaXMuaVUocy5xKGEsMCkpCmZvcihyPTE7cjxzLmdB
+KGEpOysrcil7cS5hKz0iLCIKdGhpcy5pVShzLnEoYSxyKSl9fXEuYSs9Il0ifSwKanc6ZnVuY3Rpb24o
+YSl7dmFyIHMscixxLHAsbyxuLG09dGhpcyxsPXt9CmlmKGEuZ2wwKGEpKXttLmMuYSs9Int9IgpyZXR1
+cm4hMH1zPWEuZ0EoYSkqMgpyPVAuTzgocyxudWxsLCExLHQuWCkKcT1sLmE9MApsLmI9ITAKYS5LKDAs
+bmV3IFAudGkobCxyKSkKaWYoIWwuYilyZXR1cm4hMQpwPW0uYwpwLmErPSJ7Igpmb3Iobz0nIic7cTxz
+O3ErPTIsbz0nLCInKXtwLmErPW8KbS5SVChILm4ocltxXSkpCnAuYSs9JyI6JwpuPXErMQppZihuPj1z
+KXJldHVybiBILk9IKHIsbikKbS5pVShyW25dKX1wLmErPSJ9IgpyZXR1cm4hMH19ClAudGkucHJvdG90
+eXBlPXsKJDI6ZnVuY3Rpb24oYSxiKXt2YXIgcyxyCmlmKHR5cGVvZiBhIT0ic3RyaW5nIil0aGlzLmEu
+Yj0hMQpzPXRoaXMuYgpyPXRoaXMuYQpDLk5tLlk1KHMsci5hKyssYSkKQy5ObS5ZNShzLHIuYSsrLGIp
+fSwKJFM6OX0KUC50dS5wcm90b3R5cGU9ewpnVks6ZnVuY3Rpb24oKXt2YXIgcz10aGlzLmMuYQpyZXR1
+cm4gcy5jaGFyQ29kZUF0KDApPT0wP3M6c319ClAudTUucHJvdG90eXBlPXsKZ1pFOmZ1bmN0aW9uKCl7
+cmV0dXJuIEMuUWt9fQpQLkUzLnByb3RvdHlwZT17CldKOmZ1bmN0aW9uKGEpe3ZhciBzLHIscSxwPVAu
+akIoMCxudWxsLGEubGVuZ3RoKSxvPXAtMAppZihvPT09MClyZXR1cm4gbmV3IFVpbnQ4QXJyYXkoMCkK
+cz1vKjMKcj1uZXcgVWludDhBcnJheShzKQpxPW5ldyBQLlJ3KHIpCmlmKHEuR3goYSwwLHApIT09cCl7
+Qy54Qi5PKGEscC0xKQpxLlJPKCl9cmV0dXJuIG5ldyBVaW50OEFycmF5KHIuc3ViYXJyYXkoMCxILnJN
+KDAscS5iLHMpKSl9fQpQLlJ3LnByb3RvdHlwZT17ClJPOmZ1bmN0aW9uKCl7dmFyIHM9dGhpcyxyPXMu
+YyxxPXMuYixwPXMuYj1xKzEsbz1yLmxlbmd0aAppZihxPj1vKXJldHVybiBILk9IKHIscSkKcltxXT0y
+MzkKcT1zLmI9cCsxCmlmKHA+PW8pcmV0dXJuIEguT0gocixwKQpyW3BdPTE5MQpzLmI9cSsxCmlmKHE+
+PW8pcmV0dXJuIEguT0gocixxKQpyW3FdPTE4OX0sCk82OmZ1bmN0aW9uKGEsYil7dmFyIHMscixxLHAs
+byxuPXRoaXMKaWYoKGImNjQ1MTIpPT09NTYzMjApe3M9NjU1MzYrKChhJjEwMjMpPDwxMCl8YiYxMDIz
+CnI9bi5jCnE9bi5iCnA9bi5iPXErMQpvPXIubGVuZ3RoCmlmKHE+PW8pcmV0dXJuIEguT0gocixxKQpy
+W3FdPXM+Pj4xOHwyNDAKcT1uLmI9cCsxCmlmKHA+PW8pcmV0dXJuIEguT0gocixwKQpyW3BdPXM+Pj4x
+MiY2M3wxMjgKcD1uLmI9cSsxCmlmKHE+PW8pcmV0dXJuIEguT0gocixxKQpyW3FdPXM+Pj42JjYzfDEy
+OApuLmI9cCsxCmlmKHA+PW8pcmV0dXJuIEguT0gocixwKQpyW3BdPXMmNjN8MTI4CnJldHVybiEwfWVs
+c2V7bi5STygpCnJldHVybiExfX0sCkd4OmZ1bmN0aW9uKGEsYixjKXt2YXIgcyxyLHEscCxvLG4sbSxs
+PXRoaXMKaWYoYiE9PWMmJihDLnhCLk8oYSxjLTEpJjY0NTEyKT09PTU1Mjk2KS0tYwpmb3Iocz1sLmMs
+cj1zLmxlbmd0aCxxPWI7cTxjOysrcSl7cD1DLnhCLlcoYSxxKQppZihwPD0xMjcpe289bC5iCmlmKG8+
+PXIpYnJlYWsKbC5iPW8rMQpzW29dPXB9ZWxzZXtvPXAmNjQ1MTIKaWYobz09PTU1Mjk2KXtpZihsLmIr
+ND5yKWJyZWFrCm49cSsxCmlmKGwuTzYocCxDLnhCLlcoYSxuKSkpcT1ufWVsc2UgaWYobz09PTU2MzIw
+KXtpZihsLmIrMz5yKWJyZWFrCmwuUk8oKX1lbHNlIGlmKHA8PTIwNDcpe289bC5iCm09bysxCmlmKG0+
+PXIpYnJlYWsKbC5iPW0KaWYobz49cilyZXR1cm4gSC5PSChzLG8pCnNbb109cD4+PjZ8MTkyCmwuYj1t
+KzEKc1ttXT1wJjYzfDEyOH1lbHNle289bC5iCmlmKG8rMj49cilicmVhawptPWwuYj1vKzEKaWYobz49
+cilyZXR1cm4gSC5PSChzLG8pCnNbb109cD4+PjEyfDIyNApvPWwuYj1tKzEKaWYobT49cilyZXR1cm4g
+SC5PSChzLG0pCnNbbV09cD4+PjYmNjN8MTI4CmwuYj1vKzEKaWYobz49cilyZXR1cm4gSC5PSChzLG8p
+CnNbb109cCY2M3wxMjh9fX1yZXR1cm4gcX19ClAuR1kucHJvdG90eXBlPXsKV0o6ZnVuY3Rpb24oYSl7
+dmFyIHMscgp0LkwuYShhKQpzPXRoaXMuYQpyPVAua3kocyxhLDAsbnVsbCkKaWYociE9bnVsbClyZXR1
+cm4gcgpyZXR1cm4gbmV3IFAuYnoocykuTmUoYSwwLG51bGwsITApfX0KUC5iei5wcm90b3R5cGU9ewpO
+ZTpmdW5jdGlvbihhLGIsYyxkKXt2YXIgcyxyLHEscCxvLG49dGhpcwp0LkwuYShhKQpzPVAuakIoYixj
+LEouSG0oYSkpCmlmKGI9PT1zKXJldHVybiIiCnI9UC5qeShhLGIscykKcT1uLmhPKHIsMCxzLWIsITAp
+CnA9bi5iCmlmKChwJjEpIT09MCl7bz1QLmo0KHApCm4uYj0wCnRocm93IEguYihQLnJyKG8sYSxiK24u
+YykpfXJldHVybiBxfSwKaE86ZnVuY3Rpb24oYSxiLGMsZCl7dmFyIHMscixxPXRoaXMKaWYoYy1iPjEw
+MDApe3M9Qy5qbi5CVShiK2MsMikKcj1xLmhPKGEsYixzLCExKQppZigocS5iJjEpIT09MClyZXR1cm4g
+cgpyZXR1cm4gcitxLmhPKGEscyxjLGQpfXJldHVybiBxLkVoKGEsYixjLGQpfSwKRWg6ZnVuY3Rpb24o
+YSxiLGMsZCl7dmFyIHMscixxLHAsbyxuLG0sbCxrPXRoaXMsaj02NTUzMyxpPWsuYixoPWsuYyxnPW5l
+dyBQLk0oIiIpLGY9YisxLGU9YS5sZW5ndGgKaWYoYjwwfHxiPj1lKXJldHVybiBILk9IKGEsYikKcz1h
+W2JdCiRsYWJlbDAkMDpmb3Iocj1rLmE7ITA7KXtmb3IoOyEwO2Y9byl7cT1DLnhCLlcoIkFBQUFBQUFB
QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB
-QUFBQUFBQUFBQUZGRkZGRkZGRkZGRkZGRkZHR0dHR0dHR0dHR0dHR0dHSEhISEhISEhISEhISEhISEhI
-SEhISEhISEhISUhISEpFRUJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQktDQ0NDQ0NDQ0NDQ0NE
-Q0xPTk5OTUVFRUVFRUVFRUVFIixzKSYzMQpoPWk8PTMyP3MmNjE2OTQ+Pj5xOihzJjYzfGg8PDYpPj4+
-MAppPUMueEIuVygiIFx4MDAwOlhFQ0NDQ0NOOmxEYiBceDAwMDpYRUNDQ0NDTnZsRGIgXHgwMDA6WEVD
-Q0NDQ046bERiIEFBQUFBXHgwMFx4MDBceDAwXHgwMFx4MDBBQUFBQTAwMDAwQUFBQUE6Ojo6OkFBQUFB
-R0cwMDBBQUFBQTAwS0tLQUFBQUFHOjo6OkFBQUFBOklJSUlBQUFBQTAwMFx4ODAwQUFBQUFceDAwXHgw
-MFx4MDBceDAwIEFBQUFBIixpK3EpCmlmKGk9PT0wKXtnLmErPUguTHcoaCkKaWYoZj09PWMpYnJlYWsg
-JGxhYmVsMCQwCmJyZWFrfWVsc2UgaWYoKGkmMSkhPT0wKXtpZihyKXN3aXRjaChpKXtjYXNlIDY5OmNh
-c2UgNjc6Zy5hKz1ILkx3KGopCmJyZWFrCmNhc2UgNjU6Zy5hKz1ILkx3KGopOy0tZgpicmVhawpkZWZh
-dWx0OnA9Zy5hKz1ILkx3KGopCmcuYT1wK0guTHcoaikKYnJlYWt9ZWxzZXtrLmI9aQprLmM9Zi0xCnJl
-dHVybiIifWk9MH1pZihmPT09YylicmVhayAkbGFiZWwwJDAKbz1mKzEKaWYoZjwwfHxmPj1lKXJldHVy
-biBILk9IKGEsZikKcz1hW2ZdfW89ZisxCmlmKGY8MHx8Zj49ZSlyZXR1cm4gSC5PSChhLGYpCnM9YVtm
-XQppZihzPDEyOCl7d2hpbGUoITApe2lmKCEobzxjKSl7bj1jCmJyZWFrfW09bysxCmlmKG88MHx8bz49
-ZSlyZXR1cm4gSC5PSChhLG8pCnM9YVtvXQppZihzPj0xMjgpe249bS0xCm89bQpicmVha31vPW19aWYo
-bi1mPDIwKWZvcihsPWY7bDxuOysrbCl7aWYobD49ZSlyZXR1cm4gSC5PSChhLGwpCmcuYSs9SC5Mdyhh
-W2xdKX1lbHNlIGcuYSs9UC5ITShhLGYsbikKaWYobj09PWMpYnJlYWsgJGxhYmVsMCQwCmY9b31lbHNl
-IGY9b31pZihkJiZpPjMyKWlmKHIpZy5hKz1ILkx3KGopCmVsc2V7ay5iPTc3CmsuYz1jCnJldHVybiIi
-fWsuYj1pCmsuYz1oCmU9Zy5hCnJldHVybiBlLmNoYXJDb2RlQXQoMCk9PTA/ZTplfX0KUC5XRi5wcm90
-b3R5cGU9ewokMjpmdW5jdGlvbihhLGIpe3ZhciBzLHIscQp0LmZvLmEoYSkKcz10aGlzLmIKcj10aGlz
-LmEKcy5hKz1yLmEKcT1zLmErPUguRWooYS5hKQpzLmE9cSsiOiAiCnMuYSs9UC5obChiKQpyLmE9Iiwg
-In0sCiRTOjQwfQpQLmlQLnByb3RvdHlwZT17CkROOmZ1bmN0aW9uKGEsYil7aWYoYj09bnVsbClyZXR1
-cm4hMQpyZXR1cm4gYiBpbnN0YW5jZW9mIFAuaVAmJnRoaXMuYT09PWIuYSYmITB9LApnaU86ZnVuY3Rp
-b24oYSl7dmFyIHM9dGhpcy5hCnJldHVybihzXkMuam4ud0cocywzMCkpJjEwNzM3NDE4MjN9LAp3OmZ1
-bmN0aW9uKGEpe3ZhciBzPXRoaXMscj1QLkdxKEgudEoocykpLHE9UC5oMChILk5TKHMpKSxwPVAuaDAo
-SC5qQShzKSksbz1QLmgwKEguSVgocykpLG49UC5oMChILmNoKHMpKSxtPVAuaDAoSC5KZChzKSksbD1Q
-LlZ4KEgubzEocykpLGs9cisiLSIrcSsiLSIrcCsiICIrbysiOiIrbisiOiIrbSsiLiIrbApyZXR1cm4g
-a319ClAuWFMucHJvdG90eXBlPXsKZ0lJOmZ1bmN0aW9uKCl7cmV0dXJuIEgudHModGhpcy4kdGhyb3du
-SnNFcnJvcil9fQpQLkM2LnByb3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7dmFyIHM9dGhpcy5hCmlmKHMh
-PW51bGwpcmV0dXJuIkFzc2VydGlvbiBmYWlsZWQ6ICIrUC5obChzKQpyZXR1cm4iQXNzZXJ0aW9uIGZh
-aWxlZCJ9fQpQLkV6LnByb3RvdHlwZT17fQpQLkxLLnByb3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7cmV0
-dXJuIlRocm93IG9mIG51bGwuIn19ClAuYy5wcm90b3R5cGU9ewpnWjpmdW5jdGlvbigpe3JldHVybiJJ
-bnZhbGlkIGFyZ3VtZW50IisoIXRoaXMuYT8iKHMpIjoiIil9LApndTpmdW5jdGlvbigpe3JldHVybiIi
-fSwKdzpmdW5jdGlvbihhKXt2YXIgcyxyLHE9dGhpcyxwPXEuYyxvPXA9PW51bGw/IiI6IiAoIitwKyIp
-IixuPXEuZCxtPW49PW51bGw/IiI6IjogIitILkVqKG4pLGw9cS5nWigpK28rbQppZighcS5hKXJldHVy
-biBsCnM9cS5ndSgpCnI9UC5obChxLmIpCnJldHVybiBsK3MrIjogIityfX0KUC5iSi5wcm90b3R5cGU9
-ewpnWjpmdW5jdGlvbigpe3JldHVybiJSYW5nZUVycm9yIn0sCmd1OmZ1bmN0aW9uKCl7dmFyIHMscj10
-aGlzLmUscT10aGlzLmYKaWYocj09bnVsbClzPXEhPW51bGw/IjogTm90IGxlc3MgdGhhbiBvciBlcXVh
-bCB0byAiK0guRWoocSk6IiIKZWxzZSBpZihxPT1udWxsKXM9IjogTm90IGdyZWF0ZXIgdGhhbiBvciBl
-cXVhbCB0byAiK0guRWoocikKZWxzZSBpZihxPnIpcz0iOiBOb3QgaW4gaW5jbHVzaXZlIHJhbmdlICIr
-SC5FaihyKSsiLi4iK0guRWoocSkKZWxzZSBzPXE8cj8iOiBWYWxpZCB2YWx1ZSByYW5nZSBpcyBlbXB0
-eSI6IjogT25seSB2YWxpZCB2YWx1ZSBpcyAiK0guRWoocikKcmV0dXJuIHN9fQpQLmVZLnByb3RvdHlw
-ZT17CmdaOmZ1bmN0aW9uKCl7cmV0dXJuIlJhbmdlRXJyb3IifSwKZ3U6ZnVuY3Rpb24oKXt2YXIgcyxy
-PUgudVAodGhpcy5iKQppZih0eXBlb2YgciE9PSJudW1iZXIiKXJldHVybiByLkooKQppZihyPDApcmV0
-dXJuIjogaW5kZXggbXVzdCBub3QgYmUgbmVnYXRpdmUiCnM9dGhpcy5mCmlmKHM9PT0wKXJldHVybiI6
-IG5vIGluZGljZXMgYXJlIHZhbGlkIgpyZXR1cm4iOiBpbmRleCBzaG91bGQgYmUgbGVzcyB0aGFuICIr
-SC5FaihzKX0sCmdBOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmZ9fQpQLm1wLnByb3RvdHlwZT17Cnc6
+QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB
+RkZGRkZGRkZGRkZGRkZGRkdHR0dHR0dHR0dHR0dHR0dISEhISEhISEhISEhISEhISEhISEhISEhISEhJ
+SEhISkVFQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCS0NDQ0NDQ0NDQ0NDQ0RDTE9OTk5NRUVF
+RUVFRUVFRUUiLHMpJjMxCmg9aTw9MzI/cyY2MTY5ND4+PnE6KHMmNjN8aDw8Nik+Pj4wCmk9Qy54Qi5X
+KCIgXHgwMDA6WEVDQ0NDQ046bERiIFx4MDAwOlhFQ0NDQ0NOdmxEYiBceDAwMDpYRUNDQ0NDTjpsRGIg
+QUFBQUFceDAwXHgwMFx4MDBceDAwXHgwMEFBQUFBMDAwMDBBQUFBQTo6Ojo6QUFBQUFHRzAwMEFBQUFB
+MDBLS0tBQUFBQUc6Ojo6QUFBQUE6SUlJSUFBQUFBMDAwXHg4MDBBQUFBQVx4MDBceDAwXHgwMFx4MDAg
+QUFBQUEiLGkrcSkKaWYoaT09PTApe2cuYSs9SC5MdyhoKQppZihmPT09YylicmVhayAkbGFiZWwwJDAK
+YnJlYWt9ZWxzZSBpZigoaSYxKSE9PTApe2lmKHIpc3dpdGNoKGkpe2Nhc2UgNjk6Y2FzZSA2NzpnLmEr
+PUguTHcoaikKYnJlYWsKY2FzZSA2NTpnLmErPUguTHcoaik7LS1mCmJyZWFrCmRlZmF1bHQ6cD1nLmEr
+PUguTHcoaikKZy5hPXArSC5MdyhqKQpicmVha31lbHNle2suYj1pCmsuYz1mLTEKcmV0dXJuIiJ9aT0w
+fWlmKGY9PT1jKWJyZWFrICRsYWJlbDAkMApvPWYrMQppZihmPDB8fGY+PWUpcmV0dXJuIEguT0goYSxm
+KQpzPWFbZl19bz1mKzEKaWYoZjwwfHxmPj1lKXJldHVybiBILk9IKGEsZikKcz1hW2ZdCmlmKHM8MTI4
+KXt3aGlsZSghMCl7aWYoIShvPGMpKXtuPWMKYnJlYWt9bT1vKzEKaWYobzwwfHxvPj1lKXJldHVybiBI
+Lk9IKGEsbykKcz1hW29dCmlmKHM+PTEyOCl7bj1tLTEKbz1tCmJyZWFrfW89bX1pZihuLWY8MjApZm9y
+KGw9ZjtsPG47KytsKXtpZihsPj1lKXJldHVybiBILk9IKGEsbCkKZy5hKz1ILkx3KGFbbF0pfWVsc2Ug
+Zy5hKz1QLkhNKGEsZixuKQppZihuPT09YylicmVhayAkbGFiZWwwJDAKZj1vfWVsc2UgZj1vfWlmKGQm
+Jmk+MzIpaWYocilnLmErPUguTHcoaikKZWxzZXtrLmI9NzcKay5jPWMKcmV0dXJuIiJ9ay5iPWkKay5j
+PWgKZT1nLmEKcmV0dXJuIGUuY2hhckNvZGVBdCgwKT09MD9lOmV9fQpQLldGLnByb3RvdHlwZT17CiQy
+OmZ1bmN0aW9uKGEsYil7dmFyIHMscixxCnQuZm8uYShhKQpzPXRoaXMuYgpyPXRoaXMuYQpxPXMuYSs9
+ci5hCnErPWEuYQpzLmE9cQpzLmE9cSsiOiAiCnMuYSs9UC5obChiKQpyLmE9IiwgIn0sCiRTOjI4fQpQ
+LmlQLnByb3RvdHlwZT17CkROOmZ1bmN0aW9uKGEsYil7aWYoYj09bnVsbClyZXR1cm4hMQpyZXR1cm4g
+YiBpbnN0YW5jZW9mIFAuaVAmJnRoaXMuYT09PWIuYSYmITB9LApnaU86ZnVuY3Rpb24oYSl7dmFyIHM9
+dGhpcy5hCnJldHVybihzXkMuam4ud0cocywzMCkpJjEwNzM3NDE4MjN9LAp3OmZ1bmN0aW9uKGEpe3Zh
+ciBzPXRoaXMscj1QLkdxKEgudEoocykpLHE9UC5oMChILk5TKHMpKSxwPVAuaDAoSC5qQShzKSksbz1Q
+LmgwKEguSVgocykpLG49UC5oMChILmNoKHMpKSxtPVAuaDAoSC5KZChzKSksbD1QLlZ4KEgubzEocykp
+LGs9cisiLSIrcSsiLSIrcCsiICIrbysiOiIrbisiOiIrbSsiLiIrbApyZXR1cm4ga319ClAuWFMucHJv
+dG90eXBlPXsKZ0lJOmZ1bmN0aW9uKCl7cmV0dXJuIEgudHModGhpcy4kdGhyb3duSnNFcnJvcil9fQpQ
+LkM2LnByb3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7dmFyIHM9dGhpcy5hCmlmKHMhPW51bGwpcmV0dXJu
+IkFzc2VydGlvbiBmYWlsZWQ6ICIrUC5obChzKQpyZXR1cm4iQXNzZXJ0aW9uIGZhaWxlZCJ9fQpQLkV6
+LnByb3RvdHlwZT17fQpQLkYucHJvdG90eXBlPXsKdzpmdW5jdGlvbihhKXtyZXR1cm4iVGhyb3cgb2Yg
+bnVsbC4ifX0KUC5BVC5wcm90b3R5cGU9ewpnWjpmdW5jdGlvbigpe3JldHVybiJJbnZhbGlkIGFyZ3Vt
+ZW50IisoIXRoaXMuYT8iKHMpIjoiIil9LApndTpmdW5jdGlvbigpe3JldHVybiIifSwKdzpmdW5jdGlv
+bihhKXt2YXIgcyxyLHE9dGhpcyxwPXEuYyxvPXA9PW51bGw/IiI6IiAoIitwKyIpIixuPXEuZCxtPW49
+PW51bGw/IiI6IjogIitILkVqKG4pLGw9cS5nWigpK28rbQppZighcS5hKXJldHVybiBsCnM9cS5ndSgp
+CnI9UC5obChxLmIpCnJldHVybiBsK3MrIjogIityfX0KUC5iSi5wcm90b3R5cGU9ewpnWjpmdW5jdGlv
+bigpe3JldHVybiJSYW5nZUVycm9yIn0sCmd1OmZ1bmN0aW9uKCl7dmFyIHMscj10aGlzLmUscT10aGlz
+LmYKaWYocj09bnVsbClzPXEhPW51bGw/IjogTm90IGxlc3MgdGhhbiBvciBlcXVhbCB0byAiK0guRWoo
+cSk6IiIKZWxzZSBpZihxPT1udWxsKXM9IjogTm90IGdyZWF0ZXIgdGhhbiBvciBlcXVhbCB0byAiK0gu
+RWoocikKZWxzZSBpZihxPnIpcz0iOiBOb3QgaW4gaW5jbHVzaXZlIHJhbmdlICIrSC5FaihyKSsiLi4i
+K0guRWoocSkKZWxzZSBzPXE8cj8iOiBWYWxpZCB2YWx1ZSByYW5nZSBpcyBlbXB0eSI6IjogT25seSB2
+YWxpZCB2YWx1ZSBpcyAiK0guRWoocikKcmV0dXJuIHN9fQpQLmVZLnByb3RvdHlwZT17CmdaOmZ1bmN0
+aW9uKCl7cmV0dXJuIlJhbmdlRXJyb3IifSwKZ3U6ZnVuY3Rpb24oKXtpZihILklaKHRoaXMuYik8MCly
+ZXR1cm4iOiBpbmRleCBtdXN0IG5vdCBiZSBuZWdhdGl2ZSIKdmFyIHM9dGhpcy5mCmlmKHM9PT0wKXJl
+dHVybiI6IG5vIGluZGljZXMgYXJlIHZhbGlkIgpyZXR1cm4iOiBpbmRleCBzaG91bGQgYmUgbGVzcyB0
+aGFuICIrc30sCmdBOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmZ9fQpQLm1wLnByb3RvdHlwZT17Cnc6
ZnVuY3Rpb24oYSl7dmFyIHMscixxLHAsbyxuLG0sbCxrPXRoaXMsaj17fSxpPW5ldyBQLk0oIiIpCmou
YT0iIgpzPWsuYwpmb3Iocj1zLmxlbmd0aCxxPTAscD0iIixvPSIiO3E8cjsrK3Esbz0iLCAiKXtuPXNb
cV0KaS5hPXArbwpwPWkuYSs9UC5obChuKQpqLmE9IiwgIn1rLmQuSygwLG5ldyBQLldGKGosaSkpCm09
UC5obChrLmEpCmw9aS53KDApCnI9Ik5vU3VjaE1ldGhvZEVycm9yOiBtZXRob2Qgbm90IGZvdW5kOiAn
-IitILkVqKGsuYi5hKSsiJ1xuUmVjZWl2ZXI6ICIrbSsiXG5Bcmd1bWVudHM6IFsiK2wrIl0iCnJldHVy
-biByfX0KUC51Yi5wcm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEpe3JldHVybiJVbnN1cHBvcnRlZCBvcGVy
-YXRpb246ICIrdGhpcy5hfX0KUC5kcy5wcm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEpe3ZhciBzPXRoaXMu
-YQpyZXR1cm4gcyE9bnVsbD8iVW5pbXBsZW1lbnRlZEVycm9yOiAiK3M6IlVuaW1wbGVtZW50ZWRFcnJv
-ciJ9fQpQLmxqLnByb3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7cmV0dXJuIkJhZCBzdGF0ZTogIit0aGlz
-LmF9fQpQLlVWLnByb3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7dmFyIHM9dGhpcy5hCmlmKHM9PW51bGwp
-cmV0dXJuIkNvbmN1cnJlbnQgbW9kaWZpY2F0aW9uIGR1cmluZyBpdGVyYXRpb24uIgpyZXR1cm4iQ29u
-Y3VycmVudCBtb2RpZmljYXRpb24gZHVyaW5nIGl0ZXJhdGlvbjogIitQLmhsKHMpKyIuIn19ClAuazUu
-cHJvdG90eXBlPXsKdzpmdW5jdGlvbihhKXtyZXR1cm4iT3V0IG9mIE1lbW9yeSJ9LApnSUk6ZnVuY3Rp
-b24oKXtyZXR1cm4gbnVsbH0sCiRpWFM6MX0KUC5LWS5wcm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEpe3Jl
-dHVybiJTdGFjayBPdmVyZmxvdyJ9LApnSUk6ZnVuY3Rpb24oKXtyZXR1cm4gbnVsbH0sCiRpWFM6MX0K
-UC50Ny5wcm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEpe3ZhciBzPXRoaXMuYQpyZXR1cm4gcz09bnVsbD8i
-UmVhZGluZyBzdGF0aWMgdmFyaWFibGUgZHVyaW5nIGl0cyBpbml0aWFsaXphdGlvbiI6IlJlYWRpbmcg
-c3RhdGljIHZhcmlhYmxlICciK3MrIicgZHVyaW5nIGl0cyBpbml0aWFsaXphdGlvbiJ9fQpQLkNELnBy
-b3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7cmV0dXJuIkV4Y2VwdGlvbjogIit0aGlzLmF9LAokaVJ6OjF9
-ClAuYUUucHJvdG90eXBlPXsKdzpmdW5jdGlvbihhKXt2YXIgcyxyLHEscCxvLG4sbSxsLGssaixpLGgs
-Zz10aGlzLmEsZj1nIT1udWxsJiYiIiE9PWc/IkZvcm1hdEV4Y2VwdGlvbjogIitILkVqKGcpOiJGb3Jt
-YXRFeGNlcHRpb24iLGU9dGhpcy5jLGQ9dGhpcy5iCmlmKHR5cGVvZiBkPT0ic3RyaW5nIil7aWYoZSE9
-bnVsbClzPWU8MHx8ZT5kLmxlbmd0aAplbHNlIHM9ITEKaWYocyllPW51bGwKaWYoZT09bnVsbCl7aWYo
-ZC5sZW5ndGg+NzgpZD1DLnhCLk5qKGQsMCw3NSkrIi4uLiIKcmV0dXJuIGYrIlxuIitkfWZvcihyPTEs
-cT0wLHA9ITEsbz0wO288ZTsrK28pe249Qy54Qi5XKGQsbykKaWYobj09PTEwKXtpZihxIT09b3x8IXAp
-KytyCnE9bysxCnA9ITF9ZWxzZSBpZihuPT09MTMpeysrcgpxPW8rMQpwPSEwfX1mPXI+MT9mKygiIChh
-dCBsaW5lICIrcisiLCBjaGFyYWN0ZXIgIisoZS1xKzEpKyIpXG4iKTpmKygiIChhdCBjaGFyYWN0ZXIg
-IisoZSsxKSsiKVxuIikKbT1kLmxlbmd0aApmb3Iobz1lO288bTsrK28pe249Qy54Qi5PKGQsbykKaWYo
-bj09PTEwfHxuPT09MTMpe209bwpicmVha319aWYobS1xPjc4KWlmKGUtcTw3NSl7bD1xKzc1Cms9cQpq
-PSIiCmk9Ii4uLiJ9ZWxzZXtpZihtLWU8NzUpe2s9bS03NQpsPW0KaT0iIn1lbHNle2s9ZS0zNgpsPWUr
-MzYKaT0iLi4uIn1qPSIuLi4ifWVsc2V7bD1tCms9cQpqPSIiCmk9IiJ9aD1DLnhCLk5qKGQsayxsKQpy
-ZXR1cm4gZitqK2graSsiXG4iK0MueEIuVCgiICIsZS1rK2oubGVuZ3RoKSsiXlxuIn1lbHNlIHJldHVy
-biBlIT1udWxsP2YrKCIgKGF0IG9mZnNldCAiK0guRWooZSkrIikiKTpmfSwKJGlSejoxfQpQLmNYLnBy
-b3RvdHlwZT17CmRyOmZ1bmN0aW9uKGEsYil7cmV0dXJuIEguR0oodGhpcyxILkxoKHRoaXMpLkMoImNY
-LkUiKSxiKX0sCkUyOmZ1bmN0aW9uKGEsYixjKXt2YXIgcz1ILkxoKHRoaXMpCnJldHVybiBILksxKHRo
-aXMscy5LcShjKS5DKCIxKGNYLkUpIikuYShiKSxzLkMoImNYLkUiKSxjKX0sCmV2OmZ1bmN0aW9uKGEs
-Yil7dmFyIHM9SC5MaCh0aGlzKQpyZXR1cm4gbmV3IEguVTUodGhpcyxzLkMoImEyKGNYLkUpIikuYShi
-KSxzLkMoIlU1PGNYLkU+IikpfSwKdHQ6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gUC5ZMSh0aGlzLGIsSC5M
-aCh0aGlzKS5DKCJjWC5FIikpfSwKYnI6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMudHQoYSwhMCl9LApn
-QTpmdW5jdGlvbihhKXt2YXIgcyxyPXRoaXMuZ20odGhpcykKZm9yKHM9MDtyLkYoKTspKytzCnJldHVy
-biBzfSwKZ2wwOmZ1bmN0aW9uKGEpe3JldHVybiF0aGlzLmdtKHRoaXMpLkYoKX0sCmdvcjpmdW5jdGlv
-bihhKXtyZXR1cm4hdGhpcy5nbDAodGhpcyl9LAplUjpmdW5jdGlvbihhLGIpe3JldHVybiBILmJLKHRo
-aXMsYixILkxoKHRoaXMpLkMoImNYLkUiKSl9LApncjg6ZnVuY3Rpb24oYSl7dmFyIHMscj10aGlzLmdt
-KHRoaXMpCmlmKCFyLkYoKSl0aHJvdyBILmIoSC5XcCgpKQpzPXIuZ2woKQppZihyLkYoKSl0aHJvdyBI
-LmIoSC5BbSgpKQpyZXR1cm4gc30sCkU6ZnVuY3Rpb24oYSxiKXt2YXIgcyxyLHEKUC5rMShiLCJpbmRl
-eCIpCmZvcihzPXRoaXMuZ20odGhpcykscj0wO3MuRigpOyl7cT1zLmdsKCkKaWYoYj09PXIpcmV0dXJu
-IHE7KytyfXRocm93IEguYihQLkNmKGIsdGhpcywiaW5kZXgiLG51bGwscikpfSwKdzpmdW5jdGlvbihh
-KXtyZXR1cm4gUC5FUCh0aGlzLCIoIiwiKSIpfX0KUC5Bbi5wcm90b3R5cGU9e30KUC5OMy5wcm90b3R5
-cGU9ewp3OmZ1bmN0aW9uKGEpe3JldHVybiJNYXBFbnRyeSgiK0guRWoodGhpcy5hKSsiOiAiK0guRWoo
-dGhpcy5iKSsiKSJ9fQpQLmM4LnByb3RvdHlwZT17CmdpTzpmdW5jdGlvbihhKXtyZXR1cm4gUC5NaC5w
-cm90b3R5cGUuZ2lPLmNhbGwodGhpcyx0aGlzKX0sCnc6ZnVuY3Rpb24oYSl7cmV0dXJuIm51bGwifX0K
-UC5NaC5wcm90b3R5cGU9e2NvbnN0cnVjdG9yOlAuTWgsJGlNaDoxLApETjpmdW5jdGlvbihhLGIpe3Jl
-dHVybiB0aGlzPT09Yn0sCmdpTzpmdW5jdGlvbihhKXtyZXR1cm4gSC5lUSh0aGlzKX0sCnc6ZnVuY3Rp
-b24oYSl7cmV0dXJuIkluc3RhbmNlIG9mICciK0guRWooSC5saCh0aGlzKSkrIicifSwKZTc6ZnVuY3Rp
-b24oYSxiKXt0Lm8uYShiKQp0aHJvdyBILmIoUC5scih0aGlzLGIuZ1dhKCksYi5nbmQoKSxiLmdWbSgp
-KSl9LAp0b1N0cmluZzpmdW5jdGlvbigpe3JldHVybiB0aGlzLncodGhpcyl9fQpQLlpkLnByb3RvdHlw
-ZT17Cnc6ZnVuY3Rpb24oYSl7cmV0dXJuIiJ9LAokaUd6OjF9ClAuTS5wcm90b3R5cGU9ewpnQTpmdW5j
-dGlvbihhKXtyZXR1cm4gdGhpcy5hLmxlbmd0aH0sCnc6ZnVuY3Rpb24oYSl7dmFyIHM9dGhpcy5hCnJl
-dHVybiBzLmNoYXJDb2RlQXQoMCk9PTA/czpzfSwKJGlCTDoxfQpQLm4xLnByb3RvdHlwZT17CiQyOmZ1
-bmN0aW9uKGEsYil7dmFyIHMscixxLHAKdC5KLmEoYSkKSC5oKGIpCnM9Si5EMShiLCI9IikKaWYocz09
-PS0xKXtpZihiIT09IiIpYS5ZNSgwLFAua3UoYiwwLGIubGVuZ3RoLHRoaXMuYSwhMCksIiIpfWVsc2Ug
-aWYocyE9PTApe3I9Qy54Qi5OaihiLDAscykKcT1DLnhCLnluKGIscysxKQpwPXRoaXMuYQphLlk1KDAs
-UC5rdShyLDAsci5sZW5ndGgscCwhMCksUC5rdShxLDAscS5sZW5ndGgscCwhMCkpfXJldHVybiBhfSwK
-JFM6NDF9ClAuY1MucHJvdG90eXBlPXsKJDI6ZnVuY3Rpb24oYSxiKXt0aHJvdyBILmIoUC5ycigiSWxs
-ZWdhbCBJUHY0IGFkZHJlc3MsICIrYSx0aGlzLmEsYikpfSwKJFM6MjF9ClAuVkMucHJvdG90eXBlPXsK
-JDI6ZnVuY3Rpb24oYSxiKXt0aHJvdyBILmIoUC5ycigiSWxsZWdhbCBJUHY2IGFkZHJlc3MsICIrYSx0
-aGlzLmEsYikpfSwKJDE6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuJDIoYSxudWxsKX0sCiRTOjQ1fQpQ
-LkpULnByb3RvdHlwZT17CiQyOmZ1bmN0aW9uKGEsYil7dmFyIHMKaWYoYi1hPjQpdGhpcy5hLiQyKCJh
-biBJUHY2IHBhcnQgY2FuIG9ubHkgY29udGFpbiBhIG1heGltdW0gb2YgNCBoZXggZGlnaXRzIixhKQpz
-PVAuUUEoQy54Qi5Oaih0aGlzLmIsYSxiKSwxNikKaWYoczwwfHxzPjY1NTM1KXRoaXMuYS4kMigiZWFj
-aCBwYXJ0IG11c3QgYmUgaW4gdGhlIHJhbmdlIG9mIGAweDAuLjB4RkZGRmAiLGEpCnJldHVybiBzfSwK
-JFM6NTJ9ClAuRG4ucHJvdG90eXBlPXsKZ25EOmZ1bmN0aW9uKCl7dmFyIHMscixxLHA9dGhpcyxvPXAu
-eAppZihvPT09JCl7bz1wLmEKcz1vLmxlbmd0aCE9PTA/bysiOiI6IiIKcj1wLmMKcT1yPT1udWxsCmlm
-KCFxfHxvPT09ImZpbGUiKXtvPXMrIi8vIgpzPXAuYgppZihzLmxlbmd0aCE9PTApbz1vK3MrIkAiCmlm
-KCFxKW8rPXIKcz1wLmQKaWYocyE9bnVsbClvPW8rIjoiK0guRWoocyl9ZWxzZSBvPXMKbys9cC5lCnM9
-cC5mCmlmKHMhPW51bGwpbz1vKyI/IitzCnM9cC5yCmlmKHMhPW51bGwpbz1vKyIjIitzCm89by5jaGFy
-Q29kZUF0KDApPT0wP286bwppZihwLng9PT0kKXAueD1vCmVsc2Ugbz1ILnYoSC5qKCJfdGV4dCIpKX1y
-ZXR1cm4gb30sCmdGajpmdW5jdGlvbigpe3ZhciBzLHI9dGhpcyxxPXIueQppZihxPT09JCl7cz1yLmUK
-aWYocy5sZW5ndGghPT0wJiZDLnhCLlcocywwKT09PTQ3KXM9Qy54Qi55bihzLDEpCnE9cy5sZW5ndGg9
-PT0wP0MueEQ6UC5BRihuZXcgSC5sSihILlFJKHMuc3BsaXQoIi8iKSx0LnMpLHQuZE8uYShQLlBIKCkp
-LHQuZG8pLHQuTikKaWYoci55PT09JClyLnNLcChxKQplbHNlIHE9SC52KEguaigicGF0aFNlZ21lbnRz
-IikpfXJldHVybiBxfSwKZ2lPOmZ1bmN0aW9uKGEpe3ZhciBzPXRoaXMscj1zLnoKaWYocj09PSQpe3I9
-Si5oZihzLmduRCgpKQppZihzLno9PT0kKXMuej1yCmVsc2Ugcj1ILnYoSC5qKCJoYXNoQ29kZSIpKX1y
-ZXR1cm4gcn0sCmdoWTpmdW5jdGlvbigpe3ZhciBzPXRoaXMscj1zLlEKaWYocj09PSQpe3I9cy5mCnI9
-bmV3IFAuR2ooUC5XWChyPT1udWxsPyIiOnIpLHQuZHcpCmlmKHMuUT09PSQpcy5zTk0ocikKZWxzZSBy
-PUgudihILmooInF1ZXJ5UGFyYW1ldGVycyIpKX1yZXR1cm4gcn0sCmdrdTpmdW5jdGlvbigpe3JldHVy
-biB0aGlzLmJ9LApnSmY6ZnVuY3Rpb24oYSl7dmFyIHM9dGhpcy5jCmlmKHM9PW51bGwpcmV0dXJuIiIK
-aWYoQy54Qi5uQyhzLCJbIikpcmV0dXJuIEMueEIuTmoocywxLHMubGVuZ3RoLTEpCnJldHVybiBzfSwK
-Z3RwOmZ1bmN0aW9uKGEpe3ZhciBzPXRoaXMuZApyZXR1cm4gcz09bnVsbD9QLndLKHRoaXMuYSk6c30s
-Cmd0UDpmdW5jdGlvbigpe3ZhciBzPXRoaXMuZgpyZXR1cm4gcz09bnVsbD8iIjpzfSwKZ0thOmZ1bmN0
-aW9uKCl7dmFyIHM9dGhpcy5yCnJldHVybiBzPT1udWxsPyIiOnN9LApoQjpmdW5jdGlvbihhKXt2YXIg
-cz10aGlzLmEKaWYoYS5sZW5ndGghPT1zLmxlbmd0aClyZXR1cm4hMQpyZXR1cm4gUC5OUihhLHMpfSwK
-bm06ZnVuY3Rpb24oYSxiKXt2YXIgcyxyLHEscCxvLG4sbSxsLGssaj10aGlzCnQuYzkuYShiKQpzPWou
-YQpyPXM9PT0iZmlsZSIKcT1qLmIKcD1qLmQKbz1qLmMKaWYoIShvIT1udWxsKSlvPXEubGVuZ3RoIT09
-MHx8cCE9bnVsbHx8cj8iIjpudWxsCm49ai5lCmlmKCFyKW09byE9bnVsbCYmbi5sZW5ndGghPT0wCmVs
-c2UgbT0hMAppZihtJiYhQy54Qi5uQyhuLCIvIikpbj0iLyIrbgpsPW4Kaz1QLmxlKG51bGwsMCwwLGIp
-CnJldHVybiBQLkNnKHMscSxvLHAsbCxrLGoucil9LApKaDpmdW5jdGlvbihhLGIpe3ZhciBzLHIscSxw
-LG8sbgpmb3Iocz0wLHI9MDtDLnhCLlFpKGIsIi4uLyIscik7KXtyKz0zOysrc31xPUMueEIuY24oYSwi
-LyIpCndoaWxlKCEwKXtpZighKHE+MCYmcz4wKSlicmVhawpwPUMueEIuUGsoYSwiLyIscS0xKQppZihw
-PDApYnJlYWsKbz1xLXAKbj1vIT09MgppZighbnx8bz09PTMpaWYoQy54Qi5PKGEscCsxKT09PTQ2KW49
-IW58fEMueEIuTyhhLHArMik9PT00NgplbHNlIG49ITEKZWxzZSBuPSExCmlmKG4pYnJlYWs7LS1zCnE9
-cH1yZXR1cm4gQy54Qi5pNyhhLHErMSxudWxsLEMueEIueW4oYixyLTMqcykpfSwKWkk6ZnVuY3Rpb24o
-YSl7cmV0dXJuIHRoaXMubVMoUC5oSyhhKSl9LAptUzpmdW5jdGlvbihhKXt2YXIgcyxyLHEscCxvLG4s
-bSxsLGssaixpPXRoaXMsaD1udWxsCmlmKGEuZ0ZpKCkubGVuZ3RoIT09MCl7cz1hLmdGaSgpCmlmKGEu
-Z2NqKCkpe3I9YS5na3UoKQpxPWEuZ0pmKGEpCnA9YS5neEEoKT9hLmd0cChhKTpofWVsc2V7cD1oCnE9
-cApyPSIifW89UC54ZShhLmdJaShhKSkKbj1hLmdRRCgpP2EuZ3RQKCk6aH1lbHNle3M9aS5hCmlmKGEu
-Z2NqKCkpe3I9YS5na3UoKQpxPWEuZ0pmKGEpCnA9UC53QihhLmd4QSgpP2EuZ3RwKGEpOmgscykKbz1Q
-LnhlKGEuZ0lpKGEpKQpuPWEuZ1FEKCk/YS5ndFAoKTpofWVsc2V7cj1pLmIKcT1pLmMKcD1pLmQKbz1p
-LmUKaWYoYS5nSWkoYSk9PT0iIiluPWEuZ1FEKCk/YS5ndFAoKTppLmYKZWxzZXttPVAudWooaSxvKQpp
-ZihtPjApe2w9Qy54Qi5OaihvLDAsbSkKbz1hLmd0VCgpP2wrUC54ZShhLmdJaShhKSk6bCtQLnhlKGku
-SmgoQy54Qi55bihvLGwubGVuZ3RoKSxhLmdJaShhKSkpfWVsc2UgaWYoYS5ndFQoKSlvPVAueGUoYS5n
-SWkoYSkpCmVsc2UgaWYoby5sZW5ndGg9PT0wKWlmKHE9PW51bGwpbz1zLmxlbmd0aD09PTA/YS5nSWko
-YSk6UC54ZShhLmdJaShhKSkKZWxzZSBvPVAueGUoIi8iK2EuZ0lpKGEpKQplbHNle2s9aS5KaChvLGEu
-Z0lpKGEpKQpqPXMubGVuZ3RoPT09MAppZighanx8cSE9bnVsbHx8Qy54Qi5uQyhvLCIvIikpbz1QLnhl
-KGspCmVsc2Ugbz1QLndGKGssIWp8fHEhPW51bGwpfW49YS5nUUQoKT9hLmd0UCgpOmh9fX1yZXR1cm4g
-UC5DZyhzLHIscSxwLG8sbixhLmdaOCgpP2EuZ0thKCk6aCl9LApnY2o6ZnVuY3Rpb24oKXtyZXR1cm4g
-dGhpcy5jIT1udWxsfSwKZ3hBOmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuZCE9bnVsbH0sCmdRRDpmdW5j
-dGlvbigpe3JldHVybiB0aGlzLmYhPW51bGx9LApnWjg6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5yIT1u
-dWxsfSwKZ3RUOmZ1bmN0aW9uKCl7cmV0dXJuIEMueEIubkModGhpcy5lLCIvIil9LAp0NDpmdW5jdGlv
-bigpe3ZhciBzLHI9dGhpcyxxPXIuYQppZihxIT09IiImJnEhPT0iZmlsZSIpdGhyb3cgSC5iKFAuTDQo
-IkNhbm5vdCBleHRyYWN0IGEgZmlsZSBwYXRoIGZyb20gYSAiK3ErIiBVUkkiKSkKcT1yLmYKaWYoKHE9
-PW51bGw/IiI6cSkhPT0iIil0aHJvdyBILmIoUC5MNCh1LmkpKQpxPXIucgppZigocT09bnVsbD8iIjpx
-KSE9PSIiKXRocm93IEguYihQLkw0KHUubCkpCnE9JC53USgpCmlmKEgub1QocSkpcT1QLm1uKHIpCmVs
-c2V7aWYoci5jIT1udWxsJiZyLmdKZihyKSE9PSIiKUgudihQLkw0KHUuaikpCnM9ci5nRmooKQpQLmtF
-KHMsITEpCnE9UC5sKEMueEIubkMoci5lLCIvIik/Ii8iOiIiLHMsIi8iKQpxPXEuY2hhckNvZGVBdCgw
-KT09MD9xOnF9cmV0dXJuIHF9LAp3OmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmduRCgpfSwKRE46ZnVu
-Y3Rpb24oYSxiKXt2YXIgcyxyLHE9dGhpcwppZihiPT1udWxsKXJldHVybiExCmlmKHE9PT1iKXJldHVy
-biEwCmlmKHQuZEQuYihiKSlpZihxLmE9PT1iLmdGaSgpKWlmKHEuYyE9bnVsbD09PWIuZ2NqKCkpaWYo
-cS5iPT09Yi5na3UoKSlpZihxLmdKZihxKT09PWIuZ0pmKGIpKWlmKHEuZ3RwKHEpPT09Yi5ndHAoYikp
-aWYocS5lPT09Yi5nSWkoYikpe3M9cS5mCnI9cz09bnVsbAppZighcj09PWIuZ1FEKCkpe2lmKHIpcz0i
-IgppZihzPT09Yi5ndFAoKSl7cz1xLnIKcj1zPT1udWxsCmlmKCFyPT09Yi5nWjgoKSl7aWYocilzPSIi
-CnM9cz09PWIuZ0thKCl9ZWxzZSBzPSExfWVsc2Ugcz0hMX1lbHNlIHM9ITF9ZWxzZSBzPSExCmVsc2Ug
-cz0hMQplbHNlIHM9ITEKZWxzZSBzPSExCmVsc2Ugcz0hMQplbHNlIHM9ITEKZWxzZSBzPSExCnJldHVy
-biBzfSwKc0twOmZ1bmN0aW9uKGEpe3RoaXMueT10LmJrLmEoYSl9LApzTk06ZnVuY3Rpb24oYSl7dGhp
-cy5RPXQuY1ouYShhKX0sCiRpaUQ6MSwKZ0ZpOmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuYX0sCmdJaTpm
-dW5jdGlvbihhKXtyZXR1cm4gdGhpcy5lfX0KUC5SWi5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXty
-ZXR1cm4gUC5lUChDLlpKLEguaChhKSxDLnhNLCExKX0sCiRTOjV9ClAuTUUucHJvdG90eXBlPXsKJDI6
-ZnVuY3Rpb24oYSxiKXt2YXIgcz10aGlzLmIscj10aGlzLmEKcy5hKz1yLmEKci5hPSImIgpyPXMuYSs9
-SC5FaihQLmVQKEMuRjMsYSxDLnhNLCEwKSkKaWYoYiE9bnVsbCYmYi5sZW5ndGghPT0wKXtzLmE9cisi
-PSIKcy5hKz1ILkVqKFAuZVAoQy5GMyxiLEMueE0sITApKX19LAokUzoyMn0KUC55NS5wcm90b3R5cGU9
-ewokMjpmdW5jdGlvbihhLGIpe3ZhciBzLHIKSC5oKGEpCmlmKGI9PW51bGx8fHR5cGVvZiBiPT0ic3Ry
-aW5nIil0aGlzLmEuJDIoYSxILmsoYikpCmVsc2UgZm9yKHM9Si5JVCh0LnUuYShiKSkscj10aGlzLmE7
-cy5GKCk7KXIuJDIoYSxILmgocy5nbCgpKSl9LAokUzoxM30KUC5QRS5wcm90b3R5cGU9ewpnbFI6ZnVu
-Y3Rpb24oKXt2YXIgcyxyLHEscCxvPXRoaXMsbj1udWxsLG09by5jCmlmKG09PW51bGwpe209by5iCmlm
-KDA+PW0ubGVuZ3RoKXJldHVybiBILk9IKG0sMCkKcz1vLmEKbT1tWzBdKzEKcj1DLnhCLlhVKHMsIj8i
-LG0pCnE9cy5sZW5ndGgKaWYocj49MCl7cD1QLlBJKHMscisxLHEsQy5WQywhMSkKcT1yfWVsc2UgcD1u
-Cm09by5jPW5ldyBQLnFlKCJkYXRhIiwiIixuLG4sUC5QSShzLG0scSxDLldkLCExKSxwLG4pfXJldHVy
-biBtfSwKdzpmdW5jdGlvbihhKXt2YXIgcyxyPXRoaXMuYgppZigwPj1yLmxlbmd0aClyZXR1cm4gSC5P
-SChyLDApCnM9dGhpcy5hCnJldHVybiByWzBdPT09LTE/ImRhdGE6IitzOnN9fQpQLnlJLnByb3RvdHlw
-ZT17CiQyOmZ1bmN0aW9uKGEsYil7dmFyIHM9dGhpcy5hCmlmKGE+PXMubGVuZ3RoKXJldHVybiBILk9I
-KHMsYSkKcz1zW2FdCkMuTkEuZHUocywwLDk2LGIpCnJldHVybiBzfSwKJFM6MjN9ClAuYzYucHJvdG90
-eXBlPXsKJDM6ZnVuY3Rpb24oYSxiLGMpe3ZhciBzLHIscQpmb3Iocz1iLmxlbmd0aCxyPTA7cjxzOysr
-cil7cT1DLnhCLlcoYixyKV45NgppZihxPj05NilyZXR1cm4gSC5PSChhLHEpCmFbcV09Y319LAokUzox
-NH0KUC5xZC5wcm90b3R5cGU9ewokMzpmdW5jdGlvbihhLGIsYyl7dmFyIHMscixxCmZvcihzPUMueEIu
-VyhiLDApLHI9Qy54Qi5XKGIsMSk7czw9cjsrK3Mpe3E9KHNeOTYpPj4+MAppZihxPj05NilyZXR1cm4g
-SC5PSChhLHEpCmFbcV09Y319LAokUzoxNH0KUC5VZi5wcm90b3R5cGU9ewpnY2o6ZnVuY3Rpb24oKXty
-ZXR1cm4gdGhpcy5jPjB9LApneEE6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5jPjAmJnRoaXMuZCsxPHRo
-aXMuZX0sCmdRRDpmdW5jdGlvbigpe3JldHVybiB0aGlzLmY8dGhpcy5yfSwKZ1o4OmZ1bmN0aW9uKCl7
-cmV0dXJuIHRoaXMucjx0aGlzLmEubGVuZ3RofSwKZ3RUOmZ1bmN0aW9uKCl7cmV0dXJuIEMueEIuUWko
-dGhpcy5hLCIvIix0aGlzLmUpfSwKZ0ZpOmZ1bmN0aW9uKCl7dmFyIHM9dGhpcy54CnJldHVybiBzPT1u
-dWxsP3RoaXMueD10aGlzLlUyKCk6c30sClUyOmZ1bmN0aW9uKCl7dmFyIHMscj10aGlzLHE9ci5iCmlm
-KHE8PTApcmV0dXJuIiIKcz1xPT09NAppZihzJiZDLnhCLm5DKHIuYSwiaHR0cCIpKXJldHVybiJodHRw
-IgppZihxPT09NSYmQy54Qi5uQyhyLmEsImh0dHBzIikpcmV0dXJuImh0dHBzIgppZihzJiZDLnhCLm5D
-KHIuYSwiZmlsZSIpKXJldHVybiJmaWxlIgppZihxPT09NyYmQy54Qi5uQyhyLmEsInBhY2thZ2UiKSly
-ZXR1cm4icGFja2FnZSIKcmV0dXJuIEMueEIuTmooci5hLDAscSl9LApna3U6ZnVuY3Rpb24oKXt2YXIg
-cz10aGlzLmMscj10aGlzLmIrMwpyZXR1cm4gcz5yP0MueEIuTmoodGhpcy5hLHIscy0xKToiIn0sCmdK
-ZjpmdW5jdGlvbihhKXt2YXIgcz10aGlzLmMKcmV0dXJuIHM+MD9DLnhCLk5qKHRoaXMuYSxzLHRoaXMu
-ZCk6IiJ9LApndHA6ZnVuY3Rpb24oYSl7dmFyIHMscj10aGlzCmlmKHIuZ3hBKCkpcmV0dXJuIFAuUUEo
-Qy54Qi5OaihyLmEsci5kKzEsci5lKSxudWxsKQpzPXIuYgppZihzPT09NCYmQy54Qi5uQyhyLmEsImh0
-dHAiKSlyZXR1cm4gODAKaWYocz09PTUmJkMueEIubkMoci5hLCJodHRwcyIpKXJldHVybiA0NDMKcmV0
-dXJuIDB9LApnSWk6ZnVuY3Rpb24oYSl7cmV0dXJuIEMueEIuTmoodGhpcy5hLHRoaXMuZSx0aGlzLmYp
-fSwKZ3RQOmZ1bmN0aW9uKCl7dmFyIHM9dGhpcy5mLHI9dGhpcy5yCnJldHVybiBzPHI/Qy54Qi5Oaih0
-aGlzLmEscysxLHIpOiIifSwKZ0thOmZ1bmN0aW9uKCl7dmFyIHM9dGhpcy5yLHI9dGhpcy5hCnJldHVy
-biBzPHIubGVuZ3RoP0MueEIueW4ocixzKzEpOiIifSwKZ0ZqOmZ1bmN0aW9uKCl7dmFyIHMscixxPXRo
-aXMuZSxwPXRoaXMuZixvPXRoaXMuYQppZihDLnhCLlFpKG8sIi8iLHEpKSsrcQppZihxPT09cClyZXR1
-cm4gQy54RApzPUguUUkoW10sdC5zKQpmb3Iocj1xO3I8cDsrK3IpaWYoQy54Qi5PKG8scik9PT00Nyl7
-Qy5ObS5pKHMsQy54Qi5OaihvLHEscikpCnE9cisxfUMuTm0uaShzLEMueEIuTmoobyxxLHApKQpyZXR1
-cm4gUC5BRihzLHQuTil9LApnaFk6ZnVuY3Rpb24oKXtpZih0aGlzLmY+PXRoaXMucilyZXR1cm4gQy5D
-TQpyZXR1cm4gbmV3IFAuR2ooUC5XWCh0aGlzLmd0UCgpKSx0LmR3KX0sCmtYOmZ1bmN0aW9uKGEpe3Zh
-ciBzPXRoaXMuZCsxCnJldHVybiBzK2EubGVuZ3RoPT09dGhpcy5lJiZDLnhCLlFpKHRoaXMuYSxhLHMp
-fSwKTjk6ZnVuY3Rpb24oKXt2YXIgcz10aGlzLHI9cy5yLHE9cy5hCmlmKHI+PXEubGVuZ3RoKXJldHVy
-biBzCnJldHVybiBuZXcgUC5VZihDLnhCLk5qKHEsMCxyKSxzLmIscy5jLHMuZCxzLmUscy5mLHIscy54
-KX0sCm5tOmZ1bmN0aW9uKGEsYil7dmFyIHMscixxLHAsbyxuLG0sbCxrLGosaT10aGlzLGg9bnVsbAp0
-LmM5LmEoYikKcz1pLmdGaSgpCnI9cz09PSJmaWxlIgpxPWkuYwpwPXE+MD9DLnhCLk5qKGkuYSxpLmIr
-MyxxKToiIgpvPWkuZ3hBKCk/aS5ndHAoaSk6aApxPWkuYwppZihxPjApbj1DLnhCLk5qKGkuYSxxLGku
-ZCkKZWxzZSBuPXAubGVuZ3RoIT09MHx8byE9bnVsbHx8cj8iIjpoCnE9aS5hCm09Qy54Qi5OaihxLGku
-ZSxpLmYpCmlmKCFyKWw9biE9bnVsbCYmbS5sZW5ndGghPT0wCmVsc2UgbD0hMAppZihsJiYhQy54Qi5u
-QyhtLCIvIikpbT0iLyIrbQprPVAubGUoaCwwLDAsYikKbD1pLnIKaj1sPHEubGVuZ3RoP0MueEIueW4o
-cSxsKzEpOmgKcmV0dXJuIFAuQ2cocyxwLG4sbyxtLGssail9LApaSTpmdW5jdGlvbihhKXtyZXR1cm4g
-dGhpcy5tUyhQLmhLKGEpKX0sCm1TOmZ1bmN0aW9uKGEpe2lmKGEgaW5zdGFuY2VvZiBQLlVmKXJldHVy
-biB0aGlzLnUxKHRoaXMsYSkKcmV0dXJuIHRoaXMuUmUoKS5tUyhhKX0sCnUxOmZ1bmN0aW9uKGEsYil7
-dmFyIHMscixxLHAsbyxuLG0sbCxrLGosaSxoLGcsZixlLGQsYz1iLmIKaWYoYz4wKXJldHVybiBiCnM9
-Yi5jCmlmKHM+MCl7cj1hLmIKaWYocjw9MClyZXR1cm4gYgpxPXI9PT00CmlmKHEmJkMueEIubkMoYS5h
-LCJmaWxlIikpcD1iLmUhPT1iLmYKZWxzZSBpZihxJiZDLnhCLm5DKGEuYSwiaHR0cCIpKXA9IWIua1go
-IjgwIikKZWxzZSBwPSEocj09PTUmJkMueEIubkMoYS5hLCJodHRwcyIpKXx8IWIua1goIjQ0MyIpCmlm
-KHApe289cisxCnJldHVybiBuZXcgUC5VZihDLnhCLk5qKGEuYSwwLG8pK0MueEIueW4oYi5hLGMrMSks
-cixzK28sYi5kK28sYi5lK28sYi5mK28sYi5yK28sYS54KX1lbHNlIHJldHVybiB0aGlzLlJlKCkubVMo
-Yil9bj1iLmUKYz1iLmYKaWYobj09PWMpe3M9Yi5yCmlmKGM8cyl7cj1hLmYKbz1yLWMKcmV0dXJuIG5l
-dyBQLlVmKEMueEIuTmooYS5hLDAscikrQy54Qi55bihiLmEsYyksYS5iLGEuYyxhLmQsYS5lLGMrbyxz
-K28sYS54KX1jPWIuYQppZihzPGMubGVuZ3RoKXtyPWEucgpyZXR1cm4gbmV3IFAuVWYoQy54Qi5Oaihh
-LmEsMCxyKStDLnhCLnluKGMscyksYS5iLGEuYyxhLmQsYS5lLGEuZixzKyhyLXMpLGEueCl9cmV0dXJu
-IGEuTjkoKX1zPWIuYQppZihDLnhCLlFpKHMsIi8iLG4pKXttPWEuZQpsPVAuUngodGhpcykKaz1sPjA/
-bDptCm89ay1uCnJldHVybiBuZXcgUC5VZihDLnhCLk5qKGEuYSwwLGspK0MueEIueW4ocyxuKSxhLmIs
-YS5jLGEuZCxtLGMrbyxiLnIrbyxhLngpfWo9YS5lCmk9YS5mCmlmKGo9PT1pJiZhLmM+MCl7Zm9yKDtD
-LnhCLlFpKHMsIi4uLyIsbik7KW4rPTMKbz1qLW4rMQpyZXR1cm4gbmV3IFAuVWYoQy54Qi5OaihhLmEs
-MCxqKSsiLyIrQy54Qi55bihzLG4pLGEuYixhLmMsYS5kLGosYytvLGIucitvLGEueCl9aD1hLmEKbD1Q
-LlJ4KHRoaXMpCmlmKGw+PTApZz1sCmVsc2UgZm9yKGc9ajtDLnhCLlFpKGgsIi4uLyIsZyk7KWcrPTMK
-Zj0wCndoaWxlKCEwKXtlPW4rMwppZighKGU8PWMmJkMueEIuUWkocywiLi4vIixuKSkpYnJlYWs7Kytm
-Cm49ZX1mb3IoZD0iIjtpPmc7KXstLWkKaWYoQy54Qi5PKGgsaSk9PT00Nyl7aWYoZj09PTApe2Q9Ii8i
-CmJyZWFrfS0tZgpkPSIvIn19aWYoaT09PWcmJmEuYjw9MCYmIUMueEIuUWkoaCwiLyIsaikpe24tPWYq
-MwpkPSIifW89aS1uK2QubGVuZ3RoCnJldHVybiBuZXcgUC5VZihDLnhCLk5qKGgsMCxpKStkK0MueEIu
-eW4ocyxuKSxhLmIsYS5jLGEuZCxqLGMrbyxiLnIrbyxhLngpfSwKdDQ6ZnVuY3Rpb24oKXt2YXIgcyxy
-LHE9dGhpcyxwPXEuYgppZihwPj0wKXtzPSEocD09PTQmJkMueEIubkMocS5hLCJmaWxlIikpCnA9c31l
-bHNlIHA9ITEKaWYocCl0aHJvdyBILmIoUC5MNCgiQ2Fubm90IGV4dHJhY3QgYSBmaWxlIHBhdGggZnJv
-bSBhICIrcS5nRmkoKSsiIFVSSSIpKQpwPXEuZgpzPXEuYQppZihwPHMubGVuZ3RoKXtpZihwPHEucil0
-aHJvdyBILmIoUC5MNCh1LmkpKQp0aHJvdyBILmIoUC5MNCh1LmwpKX1yPSQud1EoKQppZihILm9UKHIp
-KXA9UC5tbihxKQplbHNle2lmKHEuYzxxLmQpSC52KFAuTDQodS5qKSkKcD1DLnhCLk5qKHMscS5lLHAp
-fXJldHVybiBwfSwKZ2lPOmZ1bmN0aW9uKGEpe3ZhciBzPXRoaXMueQpyZXR1cm4gcz09bnVsbD90aGlz
-Lnk9Qy54Qi5naU8odGhpcy5hKTpzfSwKRE46ZnVuY3Rpb24oYSxiKXtpZihiPT1udWxsKXJldHVybiEx
-CmlmKHRoaXM9PT1iKXJldHVybiEwCnJldHVybiB0LmRELmIoYikmJnRoaXMuYT09PWIudygwKX0sClJl
-OmZ1bmN0aW9uKCl7dmFyIHM9dGhpcyxyPW51bGwscT1zLmdGaSgpLHA9cy5na3UoKSxvPXMuYz4wP3Mu
-Z0pmKHMpOnIsbj1zLmd4QSgpP3MuZ3RwKHMpOnIsbT1zLmEsbD1zLmYsaz1DLnhCLk5qKG0scy5lLGwp
-LGo9cy5yCmw9bDxqP3MuZ3RQKCk6cgpyZXR1cm4gUC5DZyhxLHAsbyxuLGssbCxqPG0ubGVuZ3RoP3Mu
-Z0thKCk6cil9LAp3OmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmF9LAokaWlEOjF9ClAucWUucHJvdG90
-eXBlPXt9ClcucUUucHJvdG90eXBlPXt9ClcuR2gucHJvdG90eXBlPXsKc0xVOmZ1bmN0aW9uKGEsYil7
-YS5ocmVmPWJ9LAp3OmZ1bmN0aW9uKGEpe3JldHVybiBTdHJpbmcoYSl9LAokaUdoOjF9ClcuZlkucHJv
-dG90eXBlPXsKdzpmdW5jdGlvbihhKXtyZXR1cm4gU3RyaW5nKGEpfX0KVy5yWi5wcm90b3R5cGU9eyRp
-clo6MX0KVy5Bei5wcm90b3R5cGU9eyRpQXo6MX0KVy5RUC5wcm90b3R5cGU9eyRpUVA6MX0KVy5ueC5w
-cm90b3R5cGU9ewpnQTpmdW5jdGlvbihhKXtyZXR1cm4gYS5sZW5ndGh9fQpXLm9KLnByb3RvdHlwZT17
-CmdBOmZ1bmN0aW9uKGEpe3JldHVybiBhLmxlbmd0aH19ClcuaWQucHJvdG90eXBlPXt9ClcuUUYucHJv
-dG90eXBlPXt9ClcuTmgucHJvdG90eXBlPXsKdzpmdW5jdGlvbihhKXtyZXR1cm4gU3RyaW5nKGEpfX0K
-Vy5hZS5wcm90b3R5cGU9ewpEYzpmdW5jdGlvbihhLGIpe3JldHVybiBhLmNyZWF0ZUhUTUxEb2N1bWVu
-dChiKX19ClcuSUIucHJvdG90eXBlPXsKdzpmdW5jdGlvbihhKXt2YXIgcyxyPWEubGVmdApyLnRvU3Ry
-aW5nCnI9IlJlY3RhbmdsZSAoIitILkVqKHIpKyIsICIKcz1hLnRvcApzLnRvU3RyaW5nCnM9citILkVq
-KHMpKyIpICIKcj1hLndpZHRoCnIudG9TdHJpbmcKcj1zK0guRWoocikrIiB4ICIKcz1hLmhlaWdodApz
-LnRvU3RyaW5nCnJldHVybiByK0guRWoocyl9LApETjpmdW5jdGlvbihhLGIpe3ZhciBzLHIKaWYoYj09
-bnVsbClyZXR1cm4hMQppZih0LnEuYihiKSl7cz1hLmxlZnQKcy50b1N0cmluZwpyPWIubGVmdApyLnRv
-U3RyaW5nCmlmKHM9PT1yKXtzPWEudG9wCnMudG9TdHJpbmcKcj1iLnRvcApyLnRvU3RyaW5nCmlmKHM9
-PT1yKXtzPWEud2lkdGgKcy50b1N0cmluZwpyPWIud2lkdGgKci50b1N0cmluZwppZihzPT09cil7cz1h
-LmhlaWdodApzLnRvU3RyaW5nCnI9Yi5oZWlnaHQKci50b1N0cmluZwpyPXM9PT1yCnM9cn1lbHNlIHM9
-ITF9ZWxzZSBzPSExfWVsc2Ugcz0hMX1lbHNlIHM9ITEKcmV0dXJuIHN9LApnaU86ZnVuY3Rpb24oYSl7
-dmFyIHMscixxLHA9YS5sZWZ0CnAudG9TdHJpbmcKcD1DLkNELmdpTyhwKQpzPWEudG9wCnMudG9TdHJp
-bmcKcz1DLkNELmdpTyhzKQpyPWEud2lkdGgKci50b1N0cmluZwpyPUMuQ0QuZ2lPKHIpCnE9YS5oZWln
-aHQKcS50b1N0cmluZwpyZXR1cm4gVy5yRShwLHMscixDLkNELmdpTyhxKSl9LAokaXRuOjF9ClcuTlEu
-cHJvdG90eXBlPXsKZ0E6ZnVuY3Rpb24oYSl7cmV0dXJuIGEubGVuZ3RofX0KVy53ei5wcm90b3R5cGU9
-ewpnQTpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5hLmxlbmd0aH0sCnE6ZnVuY3Rpb24oYSxiKXt2YXIg
-cwpILnVQKGIpCnM9dGhpcy5hCmlmKGI8MHx8Yj49cy5sZW5ndGgpcmV0dXJuIEguT0gocyxiKQpyZXR1
-cm4gdGhpcy4kdGkuYy5hKHNbYl0pfSwKWTU6ZnVuY3Rpb24oYSxiLGMpe3RoaXMuJHRpLmMuYShjKQp0
-aHJvdyBILmIoUC5MNCgiQ2Fubm90IG1vZGlmeSBsaXN0IikpfX0KVy5jdi5wcm90b3R5cGU9ewpnUWc6
-ZnVuY3Rpb24oYSl7cmV0dXJuIG5ldyBXLmk3KGEpfSwKZ1A6ZnVuY3Rpb24oYSl7cmV0dXJuIG5ldyBX
-Lkk0KGEpfSwKc1A6ZnVuY3Rpb24oYSxiKXt2YXIgcwp0LlEuYShiKQpzPXRoaXMuZ1AoYSkKcy5WMSgw
-KQpzLkZWKDAsYil9LAp3OmZ1bmN0aW9uKGEpe3JldHVybiBhLmxvY2FsTmFtZX0sCkZGOmZ1bmN0aW9u
-KGEpe3ZhciBzPSEhYS5zY3JvbGxJbnRvVmlld0lmTmVlZGVkCmlmKHMpYS5zY3JvbGxJbnRvVmlld0lm
-TmVlZGVkKCkKZWxzZSBhLnNjcm9sbEludG9WaWV3KCl9LApuejpmdW5jdGlvbihhLGIsYyxkLGUpe3Zh
-ciBzLHI9dGhpcy5yNihhLGMsZCxlKQpzd2l0Y2goYi50b0xvd2VyQ2FzZSgpKXtjYXNlImJlZm9yZWJl
-Z2luIjpzPWEucGFyZW50Tm9kZQpzLnRvU3RyaW5nCkouRWgocyxyLGEpCmJyZWFrCmNhc2UiYWZ0ZXJi
-ZWdpbiI6cz1hLmNoaWxkTm9kZXMKdGhpcy5tSyhhLHIscy5sZW5ndGg+MD9zWzBdOm51bGwpCmJyZWFr
-CmNhc2UiYmVmb3JlZW5kIjphLmFwcGVuZENoaWxkKHIpCmJyZWFrCmNhc2UiYWZ0ZXJlbmQiOnM9YS5w
-YXJlbnROb2RlCnMudG9TdHJpbmcKSi5FaChzLHIsYS5uZXh0U2libGluZykKYnJlYWsKZGVmYXVsdDpI
-LnYoUC54WSgiSW52YWxpZCBwb3NpdGlvbiAiK2IpKX19LApyNjpmdW5jdGlvbihhLGIsYyxkKXt2YXIg
-cyxyLHEscAppZihjPT1udWxsKXtpZihkPT1udWxsKXtzPSQubHQKaWYocz09bnVsbCl7cz1ILlFJKFtd
-LHQudikKcj1uZXcgVy52RChzKQpDLk5tLmkocyxXLlR3KG51bGwpKQpDLk5tLmkocyxXLkJsKCkpCiQu
-bHQ9cgpkPXJ9ZWxzZSBkPXN9cz0kLkVVCmlmKHM9PW51bGwpe3M9bmV3IFcuS28oZCkKJC5FVT1zCmM9
-c31lbHNle3MuYT1kCmM9c319ZWxzZSBpZihkIT1udWxsKXRocm93IEguYihQLnhZKCJ2YWxpZGF0b3Ig
-Y2FuIG9ubHkgYmUgcGFzc2VkIGlmIHRyZWVTYW5pdGl6ZXIgaXMgbnVsbCIpKQppZigkLnhvPT1udWxs
-KXtzPWRvY3VtZW50CnI9cy5pbXBsZW1lbnRhdGlvbgpyLnRvU3RyaW5nCnI9Qy5tSC5EYyhyLCIiKQok
-LnhvPXIKJC5CTz1yLmNyZWF0ZVJhbmdlKCkKcj0kLnhvLmNyZWF0ZUVsZW1lbnQoImJhc2UiKQp0LmNS
-LmEocikKcz1zLmJhc2VVUkkKcy50b1N0cmluZwpyLmhyZWY9cwokLnhvLmhlYWQuYXBwZW5kQ2hpbGQo
-cil9cz0kLnhvCmlmKHMuYm9keT09bnVsbCl7cj1zLmNyZWF0ZUVsZW1lbnQoImJvZHkiKQpDLkJaLnNH
-UyhzLHQucC5hKHIpKX1zPSQueG8KaWYodC5wLmIoYSkpe3M9cy5ib2R5CnMudG9TdHJpbmcKcT1zfWVs
-c2V7cy50b1N0cmluZwpxPXMuY3JlYXRlRWxlbWVudChhLnRhZ05hbWUpCiQueG8uYm9keS5hcHBlbmRD
-aGlsZChxKX1pZigiY3JlYXRlQ29udGV4dHVhbEZyYWdtZW50IiBpbiB3aW5kb3cuUmFuZ2UucHJvdG90
-eXBlJiYhQy5ObS50ZyhDLlNxLGEudGFnTmFtZSkpeyQuQk8uc2VsZWN0Tm9kZUNvbnRlbnRzKHEpCnM9
-JC5CTwpzLnRvU3RyaW5nCnA9cy5jcmVhdGVDb250ZXh0dWFsRnJhZ21lbnQoYj09bnVsbD8ibnVsbCI6
-Yil9ZWxzZXtKLndmKHEsYikKcD0kLnhvLmNyZWF0ZURvY3VtZW50RnJhZ21lbnQoKQpmb3IoO3M9cS5m
-aXJzdENoaWxkLHMhPW51bGw7KXAuYXBwZW5kQ2hpbGQocyl9aWYocSE9PSQueG8uYm9keSlKLkx0KHEp
-CmMuUG4ocCkKZG9jdW1lbnQuYWRvcHROb2RlKHApCnJldHVybiBwfSwKQUg6ZnVuY3Rpb24oYSxiLGMp
-e3JldHVybiB0aGlzLnI2KGEsYixjLG51bGwpfSwKc2hmOmZ1bmN0aW9uKGEsYil7dGhpcy5ZQyhhLGIp
-fSwKcGs6ZnVuY3Rpb24oYSxiLGMpe3RoaXMuc2E0KGEsbnVsbCkKYS5hcHBlbmRDaGlsZCh0aGlzLnI2
-KGEsYixudWxsLGMpKX0sCllDOmZ1bmN0aW9uKGEsYil7cmV0dXJuIHRoaXMucGsoYSxiLG51bGwpfSwK
-c1JOOmZ1bmN0aW9uKGEsYil7YS5pbm5lckhUTUw9Yn0sCmduczpmdW5jdGlvbihhKXtyZXR1cm4gYS50
-YWdOYW1lfSwKZ1ZsOmZ1bmN0aW9uKGEpe3JldHVybiBuZXcgVy5ldShhLCJjbGljayIsITEsdC5rKX0s
-CiRpY3Y6MX0KVy5Ddi5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXtyZXR1cm4gdC5oLmIodC5BLmEo
-YSkpfSwKJFM6MjV9ClcuZWEucHJvdG90eXBlPXskaWVhOjF9ClcuRDAucHJvdG90eXBlPXsKT246ZnVu
-Y3Rpb24oYSxiLGMsZCl7dC5idy5hKGMpCmlmKGMhPW51bGwpdGhpcy52KGEsYixjLGQpfSwKQjpmdW5j
-dGlvbihhLGIsYyl7cmV0dXJuIHRoaXMuT24oYSxiLGMsbnVsbCl9LAp2OmZ1bmN0aW9uKGEsYixjLGQp
-e3JldHVybiBhLmFkZEV2ZW50TGlzdGVuZXIoYixILnRSKHQuYncuYShjKSwxKSxkKX0sCiRpRDA6MX0K
-Vy5oSC5wcm90b3R5cGU9eyRpaEg6MX0KVy5oNC5wcm90b3R5cGU9ewpnQTpmdW5jdGlvbihhKXtyZXR1
-cm4gYS5sZW5ndGh9fQpXLmJyLnByb3RvdHlwZT17CmdBOmZ1bmN0aW9uKGEpe3JldHVybiBhLmxlbmd0
-aH19ClcuVmIucHJvdG90eXBlPXsKc0dTOmZ1bmN0aW9uKGEsYil7YS5ib2R5PWJ9fQpXLmZKLnByb3Rv
-dHlwZT17CmVvOmZ1bmN0aW9uKGEsYixjLGQpe3JldHVybiBhLm9wZW4oYixjLCEwKX0sCiRpZko6MX0K
-Vy53YS5wcm90b3R5cGU9e30KVy5TZy5wcm90b3R5cGU9eyRpU2c6MX0KVy53Ny5wcm90b3R5cGU9ewpn
-RHI6ZnVuY3Rpb24oYSl7aWYoIm9yaWdpbiIgaW4gYSlyZXR1cm4gYS5vcmlnaW4KcmV0dXJuIEguRWoo
-YS5wcm90b2NvbCkrIi8vIitILkVqKGEuaG9zdCl9LAp3OmZ1bmN0aW9uKGEpe3JldHVybiBTdHJpbmco
-YSl9LAokaXc3OjF9ClcuQWoucHJvdG90eXBlPXskaUFqOjF9ClcuZTcucHJvdG90eXBlPXsKZ3I4OmZ1
-bmN0aW9uKGEpe3ZhciBzPXRoaXMuYSxyPXMuY2hpbGROb2Rlcy5sZW5ndGgKaWYocj09PTApdGhyb3cg
-SC5iKFAuUFYoIk5vIGVsZW1lbnRzIikpCmlmKHI+MSl0aHJvdyBILmIoUC5QVigiTW9yZSB0aGFuIG9u
-ZSBlbGVtZW50IikpCnM9cy5maXJzdENoaWxkCnMudG9TdHJpbmcKcmV0dXJuIHN9LApGVjpmdW5jdGlv
-bihhLGIpe3ZhciBzLHIscSxwLG8KdC5laC5hKGIpCmlmKGIgaW5zdGFuY2VvZiBXLmU3KXtzPWIuYQpy
-PXRoaXMuYQppZihzIT09cilmb3IocT1zLmNoaWxkTm9kZXMubGVuZ3RoLHA9MDtwPHE7KytwKXtvPXMu
-Zmlyc3RDaGlsZApvLnRvU3RyaW5nCnIuYXBwZW5kQ2hpbGQobyl9cmV0dXJufWZvcihzPWIuZ20oYiks
-cj10aGlzLmE7cy5GKCk7KXIuYXBwZW5kQ2hpbGQocy5nbCgpKX0sClk1OmZ1bmN0aW9uKGEsYixjKXt2
-YXIgcyxyCnQuQS5hKGMpCnM9dGhpcy5hCnI9cy5jaGlsZE5vZGVzCmlmKGI8MHx8Yj49ci5sZW5ndGgp
-cmV0dXJuIEguT0gocixiKQpzLnJlcGxhY2VDaGlsZChjLHJbYl0pfSwKZ206ZnVuY3Rpb24oYSl7dmFy
-IHM9dGhpcy5hLmNoaWxkTm9kZXMKcmV0dXJuIG5ldyBXLlc5KHMscy5sZW5ndGgsSC56SyhzKS5DKCJX
-OTxHbS5FPiIpKX0sCmdBOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmEuY2hpbGROb2Rlcy5sZW5ndGh9
-LApxOmZ1bmN0aW9uKGEsYil7dmFyIHMKSC51UChiKQpzPXRoaXMuYS5jaGlsZE5vZGVzCmlmKGI8MHx8
-Yj49cy5sZW5ndGgpcmV0dXJuIEguT0gocyxiKQpyZXR1cm4gc1tiXX19ClcudUgucHJvdG90eXBlPXsK
-d2c6ZnVuY3Rpb24oYSl7dmFyIHM9YS5wYXJlbnROb2RlCmlmKHMhPW51bGwpcy5yZW1vdmVDaGlsZChh
-KX0sCkQ0OmZ1bmN0aW9uKGEpe3ZhciBzCmZvcig7cz1hLmZpcnN0Q2hpbGQscyE9bnVsbDspYS5yZW1v
-dmVDaGlsZChzKX0sCnc6ZnVuY3Rpb24oYSl7dmFyIHM9YS5ub2RlVmFsdWUKcmV0dXJuIHM9PW51bGw/
-dGhpcy5VKGEpOnN9LApzYTQ6ZnVuY3Rpb24oYSxiKXthLnRleHRDb250ZW50PWJ9LAptSzpmdW5jdGlv
-bihhLGIsYyl7cmV0dXJuIGEuaW5zZXJ0QmVmb3JlKGIsYyl9LAokaXVIOjF9ClcuQkgucHJvdG90eXBl
-PXsKZ0E6ZnVuY3Rpb24oYSl7cmV0dXJuIGEubGVuZ3RofSwKcTpmdW5jdGlvbihhLGIpe0gudVAoYikK
-aWYoYj4+PjAhPT1ifHxiPj1hLmxlbmd0aCl0aHJvdyBILmIoUC5DZihiLGEsbnVsbCxudWxsLG51bGwp
-KQpyZXR1cm4gYVtiXX0sClk1OmZ1bmN0aW9uKGEsYixjKXt0LkEuYShjKQp0aHJvdyBILmIoUC5MNCgi
-Q2Fubm90IGFzc2lnbiBlbGVtZW50IG9mIGltbXV0YWJsZSBMaXN0LiIpKX0sCmd0SDpmdW5jdGlvbihh
-KXtpZihhLmxlbmd0aD4wKXJldHVybiBhWzBdCnRocm93IEguYihQLlBWKCJObyBlbGVtZW50cyIpKX0s
-CkU6ZnVuY3Rpb24oYSxiKXtpZihiPDB8fGI+PWEubGVuZ3RoKXJldHVybiBILk9IKGEsYikKcmV0dXJu
-IGFbYl19LAokaWJROjEsCiRpWGo6MSwKJGljWDoxLAokaXpNOjF9ClcuU04ucHJvdG90eXBlPXt9Clcu
-ZXcucHJvdG90eXBlPXskaWV3OjF9ClcubHAucHJvdG90eXBlPXsKZ0E6ZnVuY3Rpb24oYSl7cmV0dXJu
-IGEubGVuZ3RofX0KVy5UYi5wcm90b3R5cGU9ewpyNjpmdW5jdGlvbihhLGIsYyxkKXt2YXIgcyxyCmlm
+IitrLmIuYSsiJ1xuUmVjZWl2ZXI6ICIrbSsiXG5Bcmd1bWVudHM6IFsiK2wrIl0iCnJldHVybiByfX0K
+UC51Yi5wcm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEpe3JldHVybiJVbnN1cHBvcnRlZCBvcGVyYXRpb246
+ICIrdGhpcy5hfX0KUC5kcy5wcm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEpe3ZhciBzPSJVbmltcGxlbWVu
+dGVkRXJyb3I6ICIrdGhpcy5hCnJldHVybiBzfX0KUC5sai5wcm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEp
+e3JldHVybiJCYWQgc3RhdGU6ICIrdGhpcy5hfX0KUC5VVi5wcm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEp
+e3ZhciBzPXRoaXMuYQppZihzPT1udWxsKXJldHVybiJDb25jdXJyZW50IG1vZGlmaWNhdGlvbiBkdXJp
+bmcgaXRlcmF0aW9uLiIKcmV0dXJuIkNvbmN1cnJlbnQgbW9kaWZpY2F0aW9uIGR1cmluZyBpdGVyYXRp
+b246ICIrUC5obChzKSsiLiJ9fQpQLms1LnByb3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7cmV0dXJuIk91
+dCBvZiBNZW1vcnkifSwKZ0lJOmZ1bmN0aW9uKCl7cmV0dXJuIG51bGx9LAokaVhTOjF9ClAuS1kucHJv
+dG90eXBlPXsKdzpmdW5jdGlvbihhKXtyZXR1cm4iU3RhY2sgT3ZlcmZsb3cifSwKZ0lJOmZ1bmN0aW9u
+KCl7cmV0dXJuIG51bGx9LAokaVhTOjF9ClAucC5wcm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEpe3ZhciBz
+PSJSZWFkaW5nIHN0YXRpYyB2YXJpYWJsZSAnIit0aGlzLmErIicgZHVyaW5nIGl0cyBpbml0aWFsaXph
+dGlvbiIKcmV0dXJuIHN9fQpQLkNELnByb3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7cmV0dXJuIkV4Y2Vw
+dGlvbjogIit0aGlzLmF9LAokaVJ6OjF9ClAuYUUucHJvdG90eXBlPXsKdzpmdW5jdGlvbihhKXt2YXIg
+cyxyLHEscCxvLG4sbSxsLGssaixpLGgsZz10aGlzLmEsZj0iIiE9PWc/IkZvcm1hdEV4Y2VwdGlvbjog
+IitnOiJGb3JtYXRFeGNlcHRpb24iLGU9dGhpcy5jLGQ9dGhpcy5iCmlmKHR5cGVvZiBkPT0ic3RyaW5n
+Iil7aWYoZSE9bnVsbClzPWU8MHx8ZT5kLmxlbmd0aAplbHNlIHM9ITEKaWYocyllPW51bGwKaWYoZT09
+bnVsbCl7aWYoZC5sZW5ndGg+NzgpZD1DLnhCLk5qKGQsMCw3NSkrIi4uLiIKcmV0dXJuIGYrIlxuIitk
+fWZvcihyPTEscT0wLHA9ITEsbz0wO288ZTsrK28pe249Qy54Qi5XKGQsbykKaWYobj09PTEwKXtpZihx
+IT09b3x8IXApKytyCnE9bysxCnA9ITF9ZWxzZSBpZihuPT09MTMpeysrcgpxPW8rMQpwPSEwfX1mPXI+
+MT9mKygiIChhdCBsaW5lICIrcisiLCBjaGFyYWN0ZXIgIisoZS1xKzEpKyIpXG4iKTpmKygiIChhdCBj
+aGFyYWN0ZXIgIisoZSsxKSsiKVxuIikKbT1kLmxlbmd0aApmb3Iobz1lO288bTsrK28pe249Qy54Qi5P
+KGQsbykKaWYobj09PTEwfHxuPT09MTMpe209bwpicmVha319aWYobS1xPjc4KWlmKGUtcTw3NSl7bD1x
+Kzc1Cms9cQpqPSIiCmk9Ii4uLiJ9ZWxzZXtpZihtLWU8NzUpe2s9bS03NQpsPW0KaT0iIn1lbHNle2s9
+ZS0zNgpsPWUrMzYKaT0iLi4uIn1qPSIuLi4ifWVsc2V7bD1tCms9cQpqPSIiCmk9IiJ9aD1DLnhCLk5q
+KGQsayxsKQpyZXR1cm4gZitqK2graSsiXG4iK0MueEIuVCgiICIsZS1rK2oubGVuZ3RoKSsiXlxuIn1l
+bHNlIHJldHVybiBlIT1udWxsP2YrKCIgKGF0IG9mZnNldCAiK0guRWooZSkrIikiKTpmfSwKJGlSejox
+fQpQLmNYLnByb3RvdHlwZT17CmRyOmZ1bmN0aW9uKGEsYil7cmV0dXJuIEguR0oodGhpcyxILkxoKHRo
+aXMpLkMoImNYLkUiKSxiKX0sCkUyOmZ1bmN0aW9uKGEsYixjKXt2YXIgcz1ILkxoKHRoaXMpCnJldHVy
+biBILksxKHRoaXMscy5LcShjKS5DKCIxKGNYLkUpIikuYShiKSxzLkMoImNYLkUiKSxjKX0sCmV2OmZ1
+bmN0aW9uKGEsYil7dmFyIHM9SC5MaCh0aGlzKQpyZXR1cm4gbmV3IEguVTUodGhpcyxzLkMoImEyKGNY
+LkUpIikuYShiKSxzLkMoIlU1PGNYLkU+IikpfSwKdHQ6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gUC5ZMSh0
+aGlzLGIsSC5MaCh0aGlzKS5DKCJjWC5FIikpfSwKYnI6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMudHQo
+YSwhMCl9LApnQTpmdW5jdGlvbihhKXt2YXIgcyxyPXRoaXMuZ20odGhpcykKZm9yKHM9MDtyLkYoKTsp
+KytzCnJldHVybiBzfSwKZ2wwOmZ1bmN0aW9uKGEpe3JldHVybiF0aGlzLmdtKHRoaXMpLkYoKX0sCmdv
+cjpmdW5jdGlvbihhKXtyZXR1cm4hdGhpcy5nbDAodGhpcyl9LAplUjpmdW5jdGlvbihhLGIpe3JldHVy
+biBILmJLKHRoaXMsYixILkxoKHRoaXMpLkMoImNYLkUiKSl9LApncjg6ZnVuY3Rpb24oYSl7dmFyIHMs
+cj10aGlzLmdtKHRoaXMpCmlmKCFyLkYoKSl0aHJvdyBILmIoSC5XcCgpKQpzPXIuZ2woKQppZihyLkYo
+KSl0aHJvdyBILmIoSC5BbSgpKQpyZXR1cm4gc30sCkU6ZnVuY3Rpb24oYSxiKXt2YXIgcyxyLHEKUC5r
+MShiLCJpbmRleCIpCmZvcihzPXRoaXMuZ20odGhpcykscj0wO3MuRigpOyl7cT1zLmdsKCkKaWYoYj09
+PXIpcmV0dXJuIHE7KytyfXRocm93IEguYihQLkNmKGIsdGhpcywiaW5kZXgiLG51bGwscikpfSwKdzpm
+dW5jdGlvbihhKXtyZXR1cm4gUC5FUCh0aGlzLCIoIiwiKSIpfX0KUC5Bbi5wcm90b3R5cGU9e30KUC5O
+My5wcm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEpe3JldHVybiJNYXBFbnRyeSgiK0guRWoodGhpcy5hKSsi
+OiAiK0guRWoodGhpcy5iKSsiKSJ9fQpQLmM4LnByb3RvdHlwZT17CmdpTzpmdW5jdGlvbihhKXtyZXR1
+cm4gUC5NaC5wcm90b3R5cGUuZ2lPLmNhbGwodGhpcyx0aGlzKX0sCnc6ZnVuY3Rpb24oYSl7cmV0dXJu
+Im51bGwifX0KUC5NaC5wcm90b3R5cGU9e2NvbnN0cnVjdG9yOlAuTWgsJGlNaDoxLApETjpmdW5jdGlv
+bihhLGIpe3JldHVybiB0aGlzPT09Yn0sCmdpTzpmdW5jdGlvbihhKXtyZXR1cm4gSC5lUSh0aGlzKX0s
+Cnc6ZnVuY3Rpb24oYSl7cmV0dXJuIkluc3RhbmNlIG9mICciK0gubGgodGhpcykrIicifSwKZTc6ZnVu
+Y3Rpb24oYSxiKXt0Lm8uYShiKQp0aHJvdyBILmIoUC5scih0aGlzLGIuZ1dhKCksYi5nbmQoKSxiLmdW
+bSgpKSl9LAp0b1N0cmluZzpmdW5jdGlvbigpe3JldHVybiB0aGlzLncodGhpcyl9fQpQLlpkLnByb3Rv
+dHlwZT17Cnc6ZnVuY3Rpb24oYSl7cmV0dXJuIiJ9LAokaUd6OjF9ClAuTS5wcm90b3R5cGU9ewpnQTpm
+dW5jdGlvbihhKXtyZXR1cm4gdGhpcy5hLmxlbmd0aH0sCnc6ZnVuY3Rpb24oYSl7dmFyIHM9dGhpcy5h
+CnJldHVybiBzLmNoYXJDb2RlQXQoMCk9PTA/czpzfSwKJGlCTDoxfQpQLm4xLnByb3RvdHlwZT17CiQy
+OmZ1bmN0aW9uKGEsYil7dmFyIHMscixxLHAKdC52LmEoYSkKSC5uKGIpCnM9Qy54Qi5PWShiLCI9IikK
+aWYocz09PS0xKXtpZihiIT09IiIpYS5ZNSgwLFAua3UoYiwwLGIubGVuZ3RoLHRoaXMuYSwhMCksIiIp
+fWVsc2UgaWYocyE9PTApe3I9Qy54Qi5OaihiLDAscykKcT1DLnhCLnluKGIscysxKQpwPXRoaXMuYQph
+Llk1KDAsUC5rdShyLDAsci5sZW5ndGgscCwhMCksUC5rdShxLDAscS5sZW5ndGgscCwhMCkpfXJldHVy
+biBhfSwKJFM6MjZ9ClAuY1MucHJvdG90eXBlPXsKJDI6ZnVuY3Rpb24oYSxiKXt0aHJvdyBILmIoUC5y
+cigiSWxsZWdhbCBJUHY0IGFkZHJlc3MsICIrYSx0aGlzLmEsYikpfSwKJFM6MjR9ClAuVkMucHJvdG90
+eXBlPXsKJDI6ZnVuY3Rpb24oYSxiKXt0aHJvdyBILmIoUC5ycigiSWxsZWdhbCBJUHY2IGFkZHJlc3Ms
+ICIrYSx0aGlzLmEsYikpfSwKJDE6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuJDIoYSxudWxsKX0sCiRT
+OjE5fQpQLkpULnByb3RvdHlwZT17CiQyOmZ1bmN0aW9uKGEsYil7dmFyIHMKaWYoYi1hPjQpdGhpcy5h
+LiQyKCJhbiBJUHY2IHBhcnQgY2FuIG9ubHkgY29udGFpbiBhIG1heGltdW0gb2YgNCBoZXggZGlnaXRz
+IixhKQpzPVAuUUEoQy54Qi5Oaih0aGlzLmIsYSxiKSwxNikKaWYoczwwfHxzPjY1NTM1KXRoaXMuYS4k
+MigiZWFjaCBwYXJ0IG11c3QgYmUgaW4gdGhlIHJhbmdlIG9mIGAweDAuLjB4RkZGRmAiLGEpCnJldHVy
+biBzfSwKJFM6MjB9ClAuRG4ucHJvdG90eXBlPXsKZ25EOmZ1bmN0aW9uKCl7dmFyIHMscixxLHA9dGhp
+cyxvPXAueAppZihvPT1udWxsKXtvPXAuYQpzPW8ubGVuZ3RoIT09MD8iIitvKyI6IjoiIgpyPXAuYwpx
+PXI9PW51bGwKaWYoIXF8fG89PT0iZmlsZSIpe289cysiLy8iCnM9cC5iCmlmKHMubGVuZ3RoIT09MClv
+PW8rcysiQCIKaWYoIXEpbys9cgpzPXAuZAppZihzIT1udWxsKW89bysiOiIrSC5FaihzKX1lbHNlIG89
+cwpvKz1wLmUKcz1wLmYKaWYocyE9bnVsbClvPW8rIj8iK3MKcz1wLnIKaWYocyE9bnVsbClvPW8rIiMi
+K3MKbz1vLmNoYXJDb2RlQXQoMCk9PTA/bzpvCmlmKHAueD09bnVsbClwLng9bwplbHNlIG89SC52KEgu
+aigiX3RleHQiKSl9cmV0dXJuIG99LApnRmo6ZnVuY3Rpb24oKXt2YXIgcyxyPXRoaXMscT1yLnkKaWYo
+cT09bnVsbCl7cz1yLmUKaWYocy5sZW5ndGghPT0wJiZDLnhCLlcocywwKT09PTQ3KXM9Qy54Qi55bihz
+LDEpCnE9cy5sZW5ndGg9PT0wP0MueEQ6UC5BRihuZXcgSC5sSihILlFJKHMuc3BsaXQoIi8iKSx0LnMp
+LHQuZE8uYShQLlBIKCkpLHQuZG8pLHQuTikKaWYoci55PT1udWxsKXIuc0twKHEpCmVsc2UgcT1ILnYo
+SC5qKCJwYXRoU2VnbWVudHMiKSl9cmV0dXJuIHF9LApnaU86ZnVuY3Rpb24oYSl7dmFyIHM9dGhpcyxy
+PXMuegppZihyPT1udWxsKXtyPUMueEIuZ2lPKHMuZ25EKCkpCmlmKHMuej09bnVsbClzLno9cgplbHNl
+IHI9SC52KEguaigiaGFzaENvZGUiKSl9cmV0dXJuIHJ9LApnaFk6ZnVuY3Rpb24oKXt2YXIgcz10aGlz
+LHI9cy5RCmlmKHI9PW51bGwpe3I9cy5mCnI9bmV3IFAuR2ooUC5XWChyPT1udWxsPyIiOnIpLHQuZHcp
+CmlmKHMuUT09bnVsbClzLnNOTShyKQplbHNlIHI9SC52KEguaigicXVlcnlQYXJhbWV0ZXJzIikpfXJl
+dHVybiByfSwKZ2t1OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuYn0sCmdKZjpmdW5jdGlvbihhKXt2YXIg
+cz10aGlzLmMKaWYocz09bnVsbClyZXR1cm4iIgppZihDLnhCLm4ocywiWyIpKXJldHVybiBDLnhCLk5q
+KHMsMSxzLmxlbmd0aC0xKQpyZXR1cm4gc30sCmd0cDpmdW5jdGlvbihhKXt2YXIgcz10aGlzLmQKcmV0
+dXJuIHM9PW51bGw/UC53Syh0aGlzLmEpOnN9LApndFA6ZnVuY3Rpb24oKXt2YXIgcz10aGlzLmYKcmV0
+dXJuIHM9PW51bGw/IiI6c30sCmdLYTpmdW5jdGlvbigpe3ZhciBzPXRoaXMucgpyZXR1cm4gcz09bnVs
+bD8iIjpzfSwKaEI6ZnVuY3Rpb24oYSl7dmFyIHM9dGhpcy5hCmlmKGEubGVuZ3RoIT09cy5sZW5ndGgp
+cmV0dXJuITEKcmV0dXJuIFAuTlIoYSxzKX0sCm5tOmZ1bmN0aW9uKGEsYil7dmFyIHMscixxLHAsbyxu
+LG0sbCxrLGo9dGhpcwp0LmM5LmEoYikKcz1qLmEKcj1zPT09ImZpbGUiCnE9ai5iCnA9ai5kCm89ai5j
+CmlmKCEobyE9bnVsbCkpbz1xLmxlbmd0aCE9PTB8fHAhPW51bGx8fHI/IiI6bnVsbApuPWouZQppZigh
+ciltPW8hPW51bGwmJm4ubGVuZ3RoIT09MAplbHNlIG09ITAKaWYobSYmIUMueEIubihuLCIvIikpbj0i
+LyIrbgpsPW4Kaz1QLmxlKG51bGwsMCwwLGIpCnJldHVybiBuZXcgUC5EbihzLHEsbyxwLGwsayxqLnIp
+fSwKSmg6ZnVuY3Rpb24oYSxiKXt2YXIgcyxyLHEscCxvLG4KZm9yKHM9MCxyPTA7Qy54Qi5RaShiLCIu
+Li8iLHIpOyl7cis9MzsrK3N9cT1DLnhCLmNuKGEsIi8iKQp3aGlsZSghMCl7aWYoIShxPjAmJnM+MCkp
+YnJlYWsKcD1DLnhCLlBrKGEsIi8iLHEtMSkKaWYocDwwKWJyZWFrCm89cS1wCm49byE9PTIKaWYoIW58
+fG89PT0zKWlmKEMueEIuTyhhLHArMSk9PT00NiluPSFufHxDLnhCLk8oYSxwKzIpPT09NDYKZWxzZSBu
+PSExCmVsc2Ugbj0hMQppZihuKWJyZWFrOy0tcwpxPXB9cmV0dXJuIEMueEIuaTcoYSxxKzEsbnVsbCxD
+LnhCLnluKGIsci0zKnMpKX0sClpJOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLm1TKFAuaEsoYSkpfSwK
+bVM6ZnVuY3Rpb24oYSl7dmFyIHMscixxLHAsbyxuLG0sbCxrLGosaT10aGlzLGg9bnVsbAppZihhLmdG
+aSgpLmxlbmd0aCE9PTApe3M9YS5nRmkoKQppZihhLmdjaigpKXtyPWEuZ2t1KCkKcT1hLmdKZihhKQpw
+PWEuZ3hBKCk/YS5ndHAoYSk6aH1lbHNle3A9aApxPXAKcj0iIn1vPVAueGUoYS5nSWkoYSkpCm49YS5n
+UUQoKT9hLmd0UCgpOmh9ZWxzZXtzPWkuYQppZihhLmdjaigpKXtyPWEuZ2t1KCkKcT1hLmdKZihhKQpw
+PVAud0IoYS5neEEoKT9hLmd0cChhKTpoLHMpCm89UC54ZShhLmdJaShhKSkKbj1hLmdRRCgpP2EuZ3RQ
+KCk6aH1lbHNle3I9aS5iCnE9aS5jCnA9aS5kCm89aS5lCmlmKGEuZ0lpKGEpPT09IiIpbj1hLmdRRCgp
+P2EuZ3RQKCk6aS5mCmVsc2V7bT1QLnVqKGksbykKaWYobT4wKXtsPUMueEIuTmoobywwLG0pCm89YS5n
+dFQoKT9sK1AueGUoYS5nSWkoYSkpOmwrUC54ZShpLkpoKEMueEIueW4obyxsLmxlbmd0aCksYS5nSWko
+YSkpKX1lbHNlIGlmKGEuZ3RUKCkpbz1QLnhlKGEuZ0lpKGEpKQplbHNlIGlmKG8ubGVuZ3RoPT09MClp
+ZihxPT1udWxsKW89cy5sZW5ndGg9PT0wP2EuZ0lpKGEpOlAueGUoYS5nSWkoYSkpCmVsc2Ugbz1QLnhl
+KCIvIithLmdJaShhKSkKZWxzZXtrPWkuSmgobyxhLmdJaShhKSkKaj1zLmxlbmd0aD09PTAKaWYoIWp8
+fHEhPW51bGx8fEMueEIubihvLCIvIikpbz1QLnhlKGspCmVsc2Ugbz1QLndGKGssIWp8fHEhPW51bGwp
+fW49YS5nUUQoKT9hLmd0UCgpOmh9fX1yZXR1cm4gbmV3IFAuRG4ocyxyLHEscCxvLG4sYS5nWjgoKT9h
+LmdLYSgpOmgpfSwKZ2NqOmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuYyE9bnVsbH0sCmd4QTpmdW5jdGlv
+bigpe3JldHVybiB0aGlzLmQhPW51bGx9LApnUUQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5mIT1udWxs
+fSwKZ1o4OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuciE9bnVsbH0sCmd0VDpmdW5jdGlvbigpe3JldHVy
+biBDLnhCLm4odGhpcy5lLCIvIil9LAp0NDpmdW5jdGlvbigpe3ZhciBzLHI9dGhpcyxxPXIuYQppZihx
+IT09IiImJnEhPT0iZmlsZSIpdGhyb3cgSC5iKFAuTDQoIkNhbm5vdCBleHRyYWN0IGEgZmlsZSBwYXRo
+IGZyb20gYSAiK3ErIiBVUkkiKSkKcT1yLmYKaWYoKHE9PW51bGw/IiI6cSkhPT0iIil0aHJvdyBILmIo
+UC5MNCh1LmkpKQpxPXIucgppZigocT09bnVsbD8iIjpxKSE9PSIiKXRocm93IEguYihQLkw0KHUubCkp
+CnE9JC53USgpCmlmKHEpcT1QLm1uKHIpCmVsc2V7aWYoci5jIT1udWxsJiZyLmdKZihyKSE9PSIiKUgu
+dihQLkw0KHUuaikpCnM9ci5nRmooKQpQLmtFKHMsITEpCnE9UC5sKEMueEIubihyLmUsIi8iKT8iIisi
+LyI6IiIscywiLyIpCnE9cS5jaGFyQ29kZUF0KDApPT0wP3E6cX1yZXR1cm4gcX0sCnc6ZnVuY3Rpb24o
+YSl7cmV0dXJuIHRoaXMuZ25EKCl9LApETjpmdW5jdGlvbihhLGIpe3ZhciBzLHIscT10aGlzCmlmKGI9
+PW51bGwpcmV0dXJuITEKaWYocT09PWIpcmV0dXJuITAKaWYodC5kRC5iKGIpKWlmKHEuYT09PWIuZ0Zp
+KCkpaWYocS5jIT1udWxsPT09Yi5nY2ooKSlpZihxLmI9PT1iLmdrdSgpKWlmKHEuZ0pmKHEpPT09Yi5n
+SmYoYikpaWYocS5ndHAocSk9PT1iLmd0cChiKSlpZihxLmU9PT1iLmdJaShiKSl7cz1xLmYKcj1zPT1u
+dWxsCmlmKCFyPT09Yi5nUUQoKSl7aWYocilzPSIiCmlmKHM9PT1iLmd0UCgpKXtzPXEucgpyPXM9PW51
+bGwKaWYoIXI9PT1iLmdaOCgpKXtpZihyKXM9IiIKcz1zPT09Yi5nS2EoKX1lbHNlIHM9ITF9ZWxzZSBz
+PSExfWVsc2Ugcz0hMX1lbHNlIHM9ITEKZWxzZSBzPSExCmVsc2Ugcz0hMQplbHNlIHM9ITEKZWxzZSBz
+PSExCmVsc2Ugcz0hMQplbHNlIHM9ITEKcmV0dXJuIHN9LApzS3A6ZnVuY3Rpb24oYSl7dGhpcy55PXQu
+YmsuYShhKX0sCnNOTTpmdW5jdGlvbihhKXt0aGlzLlE9dC5jWi5hKGEpfSwKJGlpRDoxLApnRmk6ZnVu
+Y3Rpb24oKXtyZXR1cm4gdGhpcy5hfSwKZ0lpOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmV9fQpQLlJa
+LnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3JldHVybiBQLmVQKEMuWkosSC5uKGEpLEMueE0sITEp
+fSwKJFM6Mn0KUC5NRS5wcm90b3R5cGU9ewokMjpmdW5jdGlvbihhLGIpe3ZhciBzPXRoaXMuYixyPXRo
+aXMuYQpzLmErPXIuYQpyLmE9IiYiCnI9cy5hKz1QLmVQKEMuRjMsYSxDLnhNLCEwKQppZihiIT1udWxs
+JiZiLmxlbmd0aCE9PTApe3MuYT1yKyI9IgpzLmErPVAuZVAoQy5GMyxiLEMueE0sITApfX0sCiRTOjIy
+fQpQLnk1LnByb3RvdHlwZT17CiQyOmZ1bmN0aW9uKGEsYil7dmFyIHMscgpILm4oYSkKaWYoYj09bnVs
+bHx8dHlwZW9mIGI9PSJzdHJpbmciKXRoaXMuYS4kMihhLEguayhiKSkKZWxzZSBmb3Iocz1KLklUKHQu
+Ui5hKGIpKSxyPXRoaXMuYTtzLkYoKTspci4kMihhLEgubihzLmdsKCkpKX0sCiRTOjExfQpQLlBFLnBy
+b3RvdHlwZT17CmdsUjpmdW5jdGlvbigpe3ZhciBzLHIscSxwLG89dGhpcyxuPW51bGwsbT1vLmMKaWYo
+bT09bnVsbCl7bT1vLmIKaWYoMD49bS5sZW5ndGgpcmV0dXJuIEguT0gobSwwKQpzPW8uYQptPW1bMF0r
+MQpyPUMueEIuWFUocywiPyIsbSkKcT1zLmxlbmd0aAppZihyPj0wKXtwPVAuUEkocyxyKzEscSxDLlZD
+LCExKQpxPXJ9ZWxzZSBwPW4KbT1vLmM9bmV3IFAucWUoImRhdGEiLCIiLG4sbixQLlBJKHMsbSxxLEMu
+V2QsITEpLHAsbil9cmV0dXJuIG19LAp3OmZ1bmN0aW9uKGEpe3ZhciBzLHI9dGhpcy5iCmlmKDA+PXIu
+bGVuZ3RoKXJldHVybiBILk9IKHIsMCkKcz10aGlzLmEKcmV0dXJuIHJbMF09PT0tMT8iZGF0YToiK3M6
+c319ClAueUkucHJvdG90eXBlPXsKJDI6ZnVuY3Rpb24oYSxiKXt2YXIgcz10aGlzLmEKaWYoYT49cy5s
+ZW5ndGgpcmV0dXJuIEguT0gocyxhKQpzPXNbYV0KQy5OQS5kdShzLDAsOTYsYikKcmV0dXJuIHN9LAok
+UzoyM30KUC5jNi5wcm90b3R5cGU9ewokMzpmdW5jdGlvbihhLGIsYyl7dmFyIHMscixxCmZvcihzPWIu
+bGVuZ3RoLHI9MDtyPHM7KytyKXtxPUMueEIuVyhiLHIpXjk2CmlmKHE+PTk2KXJldHVybiBILk9IKGEs
+cSkKYVtxXT1jfX0sCiRTOjE4fQpQLnFkLnByb3RvdHlwZT17CiQzOmZ1bmN0aW9uKGEsYixjKXt2YXIg
+cyxyLHEKZm9yKHM9Qy54Qi5XKGIsMCkscj1DLnhCLlcoYiwxKTtzPD1yOysrcyl7cT0oc145Nik+Pj4w
+CmlmKHE+PTk2KXJldHVybiBILk9IKGEscSkKYVtxXT1jfX0sCiRTOjE4fQpQLlVmLnByb3RvdHlwZT17
+CmdjajpmdW5jdGlvbigpe3JldHVybiB0aGlzLmM+MH0sCmd4QTpmdW5jdGlvbigpe3JldHVybiB0aGlz
+LmM+MCYmdGhpcy5kKzE8dGhpcy5lfSwKZ1FEOmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuZjx0aGlzLnJ9
+LApnWjg6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5yPHRoaXMuYS5sZW5ndGh9LApndFQ6ZnVuY3Rpb24o
+KXtyZXR1cm4gQy54Qi5RaSh0aGlzLmEsIi8iLHRoaXMuZSl9LApnRmk6ZnVuY3Rpb24oKXt2YXIgcz10
+aGlzLngKcmV0dXJuIHM9PW51bGw/dGhpcy54PXRoaXMuVTIoKTpzfSwKVTI6ZnVuY3Rpb24oKXt2YXIg
+cyxyPXRoaXMscT1yLmIKaWYocTw9MClyZXR1cm4iIgpzPXE9PT00CmlmKHMmJkMueEIubihyLmEsImh0
+dHAiKSlyZXR1cm4iaHR0cCIKaWYocT09PTUmJkMueEIubihyLmEsImh0dHBzIikpcmV0dXJuImh0dHBz
+IgppZihzJiZDLnhCLm4oci5hLCJmaWxlIikpcmV0dXJuImZpbGUiCmlmKHE9PT03JiZDLnhCLm4oci5h
+LCJwYWNrYWdlIikpcmV0dXJuInBhY2thZ2UiCnJldHVybiBDLnhCLk5qKHIuYSwwLHEpfSwKZ2t1OmZ1
+bmN0aW9uKCl7dmFyIHM9dGhpcy5jLHI9dGhpcy5iKzMKcmV0dXJuIHM+cj9DLnhCLk5qKHRoaXMuYSxy
+LHMtMSk6IiJ9LApnSmY6ZnVuY3Rpb24oYSl7dmFyIHM9dGhpcy5jCnJldHVybiBzPjA/Qy54Qi5Oaih0
+aGlzLmEscyx0aGlzLmQpOiIifSwKZ3RwOmZ1bmN0aW9uKGEpe3ZhciBzLHI9dGhpcwppZihyLmd4QSgp
+KXJldHVybiBQLlFBKEMueEIuTmooci5hLHIuZCsxLHIuZSksbnVsbCkKcz1yLmIKaWYocz09PTQmJkMu
+eEIubihyLmEsImh0dHAiKSlyZXR1cm4gODAKaWYocz09PTUmJkMueEIubihyLmEsImh0dHBzIikpcmV0
+dXJuIDQ0MwpyZXR1cm4gMH0sCmdJaTpmdW5jdGlvbihhKXtyZXR1cm4gQy54Qi5Oaih0aGlzLmEsdGhp
+cy5lLHRoaXMuZil9LApndFA6ZnVuY3Rpb24oKXt2YXIgcz10aGlzLmYscj10aGlzLnIKcmV0dXJuIHM8
+cj9DLnhCLk5qKHRoaXMuYSxzKzEscik6IiJ9LApnS2E6ZnVuY3Rpb24oKXt2YXIgcz10aGlzLnIscj10
+aGlzLmEKcmV0dXJuIHM8ci5sZW5ndGg/Qy54Qi55bihyLHMrMSk6IiJ9LApnRmo6ZnVuY3Rpb24oKXt2
+YXIgcyxyLHE9dGhpcy5lLHA9dGhpcy5mLG89dGhpcy5hCmlmKEMueEIuUWkobywiLyIscSkpKytxCmlm
+KHE9PT1wKXJldHVybiBDLnhECnM9SC5RSShbXSx0LnMpCmZvcihyPXE7cjxwOysrcilpZihDLnhCLk8o
+byxyKT09PTQ3KXtDLk5tLmkocyxDLnhCLk5qKG8scSxyKSkKcT1yKzF9Qy5ObS5pKHMsQy54Qi5Oaihv
+LHEscCkpCnJldHVybiBQLkFGKHMsdC5OKX0sCmdoWTpmdW5jdGlvbigpe2lmKHRoaXMuZj49dGhpcy5y
+KXJldHVybiBDLkNNCnJldHVybiBuZXcgUC5HaihQLldYKHRoaXMuZ3RQKCkpLHQuZHcpfSwKa1g6ZnVu
+Y3Rpb24oYSl7dmFyIHM9dGhpcy5kKzEKcmV0dXJuIHMrYS5sZW5ndGg9PT10aGlzLmUmJkMueEIuUWko
+dGhpcy5hLGEscyl9LApOOTpmdW5jdGlvbigpe3ZhciBzPXRoaXMscj1zLnIscT1zLmEKaWYocj49cS5s
+ZW5ndGgpcmV0dXJuIHMKcmV0dXJuIG5ldyBQLlVmKEMueEIuTmoocSwwLHIpLHMuYixzLmMscy5kLHMu
+ZSxzLmYscixzLngpfSwKbm06ZnVuY3Rpb24oYSxiKXt2YXIgcyxyLHEscCxvLG4sbSxsLGssaixpPXRo
+aXMsaD1udWxsCnQuYzkuYShiKQpzPWkuZ0ZpKCkKcj1zPT09ImZpbGUiCnE9aS5jCnA9cT4wP0MueEIu
+TmooaS5hLGkuYiszLHEpOiIiCm89aS5neEEoKT9pLmd0cChpKTpoCnE9aS5jCmlmKHE+MCluPUMueEIu
+TmooaS5hLHEsaS5kKQplbHNlIG49cC5sZW5ndGghPT0wfHxvIT1udWxsfHxyPyIiOmgKcT1pLmEKbT1D
+LnhCLk5qKHEsaS5lLGkuZikKaWYoIXIpbD1uIT1udWxsJiZtLmxlbmd0aCE9PTAKZWxzZSBsPSEwCmlm
+KGwmJiFDLnhCLm4obSwiLyIpKW09Ii8iK20Kaz1QLmxlKGgsMCwwLGIpCmw9aS5yCmo9bDxxLmxlbmd0
+aD9DLnhCLnluKHEsbCsxKTpoCnJldHVybiBuZXcgUC5EbihzLHAsbixvLG0sayxqKX0sClpJOmZ1bmN0
+aW9uKGEpe3JldHVybiB0aGlzLm1TKFAuaEsoYSkpfSwKbVM6ZnVuY3Rpb24oYSl7aWYoYSBpbnN0YW5j
+ZW9mIFAuVWYpcmV0dXJuIHRoaXMudTEodGhpcyxhKQpyZXR1cm4gdGhpcy5SZSgpLm1TKGEpfSwKdTE6
+ZnVuY3Rpb24oYSxiKXt2YXIgcyxyLHEscCxvLG4sbSxsLGssaixpLGgsZyxmLGUsZCxjPWIuYgppZihj
+PjApcmV0dXJuIGIKcz1iLmMKaWYocz4wKXtyPWEuYgppZihyPD0wKXJldHVybiBiCnE9cj09PTQKaWYo
+cSYmQy54Qi5uKGEuYSwiZmlsZSIpKXA9Yi5lIT09Yi5mCmVsc2UgaWYocSYmQy54Qi5uKGEuYSwiaHR0
+cCIpKXA9IWIua1goIjgwIikKZWxzZSBwPSEocj09PTUmJkMueEIubihhLmEsImh0dHBzIikpfHwhYi5r
+WCgiNDQzIikKaWYocCl7bz1yKzEKcmV0dXJuIG5ldyBQLlVmKEMueEIuTmooYS5hLDAsbykrQy54Qi55
+bihiLmEsYysxKSxyLHMrbyxiLmQrbyxiLmUrbyxiLmYrbyxiLnIrbyxhLngpfWVsc2UgcmV0dXJuIHRo
+aXMuUmUoKS5tUyhiKX1uPWIuZQpjPWIuZgppZihuPT09Yyl7cz1iLnIKaWYoYzxzKXtyPWEuZgpvPXIt
+YwpyZXR1cm4gbmV3IFAuVWYoQy54Qi5OaihhLmEsMCxyKStDLnhCLnluKGIuYSxjKSxhLmIsYS5jLGEu
+ZCxhLmUsYytvLHMrbyxhLngpfWM9Yi5hCmlmKHM8Yy5sZW5ndGgpe3I9YS5yCnJldHVybiBuZXcgUC5V
+ZihDLnhCLk5qKGEuYSwwLHIpK0MueEIueW4oYyxzKSxhLmIsYS5jLGEuZCxhLmUsYS5mLHMrKHItcyks
+YS54KX1yZXR1cm4gYS5OOSgpfXM9Yi5hCmlmKEMueEIuUWkocywiLyIsbikpe209YS5lCmw9UC5SeCh0
+aGlzKQprPWw+MD9sOm0Kbz1rLW4KcmV0dXJuIG5ldyBQLlVmKEMueEIuTmooYS5hLDAsaykrQy54Qi55
+bihzLG4pLGEuYixhLmMsYS5kLG0sYytvLGIucitvLGEueCl9aj1hLmUKaT1hLmYKaWYoaj09PWkmJmEu
+Yz4wKXtmb3IoO0MueEIuUWkocywiLi4vIixuKTspbis9MwpvPWotbisxCnJldHVybiBuZXcgUC5VZihD
+LnhCLk5qKGEuYSwwLGopKyIvIitDLnhCLnluKHMsbiksYS5iLGEuYyxhLmQsaixjK28sYi5yK28sYS54
+KX1oPWEuYQpsPVAuUngodGhpcykKaWYobD49MClnPWwKZWxzZSBmb3IoZz1qO0MueEIuUWkoaCwiLi4v
+IixnKTspZys9MwpmPTAKd2hpbGUoITApe2U9biszCmlmKCEoZTw9YyYmQy54Qi5RaShzLCIuLi8iLG4p
+KSlicmVhazsrK2YKbj1lfWZvcihkPSIiO2k+Zzspey0taQppZihDLnhCLk8oaCxpKT09PTQ3KXtpZihm
+PT09MCl7ZD0iLyIKYnJlYWt9LS1mCmQ9Ii8ifX1pZihpPT09ZyYmYS5iPD0wJiYhQy54Qi5RaShoLCIv
+IixqKSl7bi09ZiozCmQ9IiJ9bz1pLW4rZC5sZW5ndGgKcmV0dXJuIG5ldyBQLlVmKEMueEIuTmooaCww
+LGkpK2QrQy54Qi55bihzLG4pLGEuYixhLmMsYS5kLGosYytvLGIucitvLGEueCl9LAp0NDpmdW5jdGlv
+bigpe3ZhciBzLHIscT10aGlzLHA9cS5iCmlmKHA+PTApe3M9IShwPT09NCYmQy54Qi5uKHEuYSwiZmls
+ZSIpKQpwPXN9ZWxzZSBwPSExCmlmKHApdGhyb3cgSC5iKFAuTDQoIkNhbm5vdCBleHRyYWN0IGEgZmls
+ZSBwYXRoIGZyb20gYSAiK3EuZ0ZpKCkrIiBVUkkiKSkKcD1xLmYKcz1xLmEKaWYocDxzLmxlbmd0aCl7
+aWYocDxxLnIpdGhyb3cgSC5iKFAuTDQodS5pKSkKdGhyb3cgSC5iKFAuTDQodS5sKSl9cj0kLndRKCkK
+aWYocilwPVAubW4ocSkKZWxzZXtpZihxLmM8cS5kKUgudihQLkw0KHUuaikpCnA9Qy54Qi5OaihzLHEu
+ZSxwKX1yZXR1cm4gcH0sCmdpTzpmdW5jdGlvbihhKXt2YXIgcz10aGlzLnkKcmV0dXJuIHM9PW51bGw/
+dGhpcy55PUMueEIuZ2lPKHRoaXMuYSk6c30sCkROOmZ1bmN0aW9uKGEsYil7aWYoYj09bnVsbClyZXR1
+cm4hMQppZih0aGlzPT09YilyZXR1cm4hMApyZXR1cm4gdC5kRC5iKGIpJiZ0aGlzLmE9PT1iLncoMCl9
+LApSZTpmdW5jdGlvbigpe3ZhciBzPXRoaXMscj1udWxsLHE9cy5nRmkoKSxwPXMuZ2t1KCksbz1zLmM+
+MD9zLmdKZihzKTpyLG49cy5neEEoKT9zLmd0cChzKTpyLG09cy5hLGw9cy5mLGs9Qy54Qi5OaihtLHMu
+ZSxsKSxqPXMucgpsPWw8aj9zLmd0UCgpOnIKcmV0dXJuIG5ldyBQLkRuKHEscCxvLG4sayxsLGo8bS5s
+ZW5ndGg/cy5nS2EoKTpyKX0sCnc6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuYX0sCiRpaUQ6MX0KUC5x
+ZS5wcm90b3R5cGU9e30KVy5xRS5wcm90b3R5cGU9e30KVy5HaC5wcm90b3R5cGU9ewpzTFU6ZnVuY3Rp
+b24oYSxiKXthLmhyZWY9Yn0sCnc6ZnVuY3Rpb24oYSl7cmV0dXJuIFN0cmluZyhhKX0sCiRpR2g6MX0K
+Vy5mWS5wcm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEpe3JldHVybiBTdHJpbmcoYSl9fQpXLnJaLnByb3Rv
+dHlwZT17JGlyWjoxfQpXLkF6LnByb3RvdHlwZT17JGlBejoxfQpXLlFQLnByb3RvdHlwZT17JGlRUDox
+fQpXLm54LnByb3RvdHlwZT17CmdBOmZ1bmN0aW9uKGEpe3JldHVybiBhLmxlbmd0aH19Clcub0oucHJv
+dG90eXBlPXsKZ0E6ZnVuY3Rpb24oYSl7cmV0dXJuIGEubGVuZ3RofX0KVy5pZC5wcm90b3R5cGU9e30K
+Vy5RRi5wcm90b3R5cGU9e30KVy5OaC5wcm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEpe3JldHVybiBTdHJp
+bmcoYSl9fQpXLmFlLnByb3RvdHlwZT17CkRjOmZ1bmN0aW9uKGEsYil7cmV0dXJuIGEuY3JlYXRlSFRN
+TERvY3VtZW50KGIpfX0KVy5JQi5wcm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEpe3ZhciBzLHI9YS5sZWZ0
+CnIudG9TdHJpbmcKcj0iUmVjdGFuZ2xlICgiK0guRWoocikrIiwgIgpzPWEudG9wCnMudG9TdHJpbmcK
+cz1yK0guRWoocykrIikgIgpyPWEud2lkdGgKci50b1N0cmluZwpyPXMrSC5FaihyKSsiIHggIgpzPWEu
+aGVpZ2h0CnMudG9TdHJpbmcKcmV0dXJuIHIrSC5FaihzKX0sCkROOmZ1bmN0aW9uKGEsYil7dmFyIHMs
+cgppZihiPT1udWxsKXJldHVybiExCmlmKHQucS5iKGIpKXtzPWEubGVmdApzLnRvU3RyaW5nCnI9Yi5s
+ZWZ0CnIudG9TdHJpbmcKaWYocz09PXIpe3M9YS50b3AKcy50b1N0cmluZwpyPWIudG9wCnIudG9TdHJp
+bmcKaWYocz09PXIpe3M9YS53aWR0aApzLnRvU3RyaW5nCnI9Yi53aWR0aApyLnRvU3RyaW5nCmlmKHM9
+PT1yKXtzPWEuaGVpZ2h0CnMudG9TdHJpbmcKcj1iLmhlaWdodApyLnRvU3RyaW5nCnI9cz09PXIKcz1y
+fWVsc2Ugcz0hMX1lbHNlIHM9ITF9ZWxzZSBzPSExfWVsc2Ugcz0hMQpyZXR1cm4gc30sCmdpTzpmdW5j
+dGlvbihhKXt2YXIgcyxyLHEscD1hLmxlZnQKcC50b1N0cmluZwpwPUMuQ0QuZ2lPKHApCnM9YS50b3AK
+cy50b1N0cmluZwpzPUMuQ0QuZ2lPKHMpCnI9YS53aWR0aApyLnRvU3RyaW5nCnI9Qy5DRC5naU8ocikK
+cT1hLmhlaWdodApxLnRvU3RyaW5nCnJldHVybiBXLnJFKHAscyxyLEMuQ0QuZ2lPKHEpKX0sCiRpdG46
+MX0KVy5uNy5wcm90b3R5cGU9ewpnQTpmdW5jdGlvbihhKXtyZXR1cm4gYS5sZW5ndGh9fQpXLnd6LnBy
+b3RvdHlwZT17CmdBOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmEubGVuZ3RofSwKcTpmdW5jdGlvbihh
+LGIpe3ZhciBzCkguSVooYikKcz10aGlzLmEKaWYoYjwwfHxiPj1zLmxlbmd0aClyZXR1cm4gSC5PSChz
+LGIpCnJldHVybiB0aGlzLiR0aS5jLmEoc1tiXSl9LApZNTpmdW5jdGlvbihhLGIsYyl7dGhpcy4kdGku
+Yy5hKGMpCnRocm93IEguYihQLkw0KCJDYW5ub3QgbW9kaWZ5IGxpc3QiKSl9fQpXLmN2LnByb3RvdHlw
+ZT17CmdRZzpmdW5jdGlvbihhKXtyZXR1cm4gbmV3IFcuaTcoYSl9LApnUDpmdW5jdGlvbihhKXtyZXR1
+cm4gbmV3IFcuSTQoYSl9LApzUDpmdW5jdGlvbihhLGIpe3ZhciBzCnQuTy5hKGIpCnM9dGhpcy5nUChh
+KQpzLlYxKDApCnMuRlYoMCxiKX0sCnc6ZnVuY3Rpb24oYSl7cmV0dXJuIGEubG9jYWxOYW1lfSwKRkY6
+ZnVuY3Rpb24oYSl7dmFyIHM9ISFhLnNjcm9sbEludG9WaWV3SWZOZWVkZWQKaWYocylhLnNjcm9sbElu
+dG9WaWV3SWZOZWVkZWQoKQplbHNlIGEuc2Nyb2xsSW50b1ZpZXcoKX0sCm56OmZ1bmN0aW9uKGEsYixj
+LGQsZSl7dmFyIHMscj10aGlzLnI2KGEsYyxkLGUpCnN3aXRjaChiLnRvTG93ZXJDYXNlKCkpe2Nhc2Ui
+YmVmb3JlYmVnaW4iOnM9YS5wYXJlbnROb2RlCnMudG9TdHJpbmcKSi5FaChzLHIsYSkKYnJlYWsKY2Fz
+ZSJhZnRlcmJlZ2luIjpzPWEuY2hpbGROb2Rlcwp0aGlzLm1LKGEscixzLmxlbmd0aD4wP3NbMF06bnVs
+bCkKYnJlYWsKY2FzZSJiZWZvcmVlbmQiOmEuYXBwZW5kQ2hpbGQocikKYnJlYWsKY2FzZSJhZnRlcmVu
+ZCI6cz1hLnBhcmVudE5vZGUKcy50b1N0cmluZwpKLkVoKHMscixhLm5leHRTaWJsaW5nKQpicmVhawpk
+ZWZhdWx0OkgudihQLnhZKCJJbnZhbGlkIHBvc2l0aW9uICIrYikpfX0sCnI2OmZ1bmN0aW9uKGEsYixj
+LGQpe3ZhciBzLHIscSxwCmlmKGM9PW51bGwpe2lmKGQ9PW51bGwpe3M9JC5sdAppZihzPT1udWxsKXtz
+PUguUUkoW10sdC5yKQpyPW5ldyBXLnZEKHMpCkMuTm0uaShzLFcuVHcobnVsbCkpCkMuTm0uaShzLFcu
+QmwoKSkKJC5sdD1yCmQ9cn1lbHNlIGQ9c31zPSQuRVUKaWYocz09bnVsbCl7cz1uZXcgVy5LbyhkKQok
+LkVVPXMKYz1zfWVsc2V7cy5hPWQKYz1zfX1lbHNlIGlmKGQhPW51bGwpdGhyb3cgSC5iKFAueFkoInZh
+bGlkYXRvciBjYW4gb25seSBiZSBwYXNzZWQgaWYgdHJlZVNhbml0aXplciBpcyBudWxsIikpCmlmKCQu
+eG89PW51bGwpe3M9ZG9jdW1lbnQKcj1zLmltcGxlbWVudGF0aW9uCnIudG9TdHJpbmcKcj1DLm1ILkRj
+KHIsIiIpCiQueG89cgokLkJPPXIuY3JlYXRlUmFuZ2UoKQpyPSQueG8uY3JlYXRlRWxlbWVudCgiYmFz
+ZSIpCnQuY1IuYShyKQpzPXMuYmFzZVVSSQpzLnRvU3RyaW5nCnIuaHJlZj1zCiQueG8uaGVhZC5hcHBl
+bmRDaGlsZChyKX1zPSQueG8KaWYocy5ib2R5PT1udWxsKXtyPXMuY3JlYXRlRWxlbWVudCgiYm9keSIp
+CkMuQlouc0dTKHMsdC5rLmEocikpfXM9JC54bwppZih0LmsuYihhKSl7cz1zLmJvZHkKcy50b1N0cmlu
+ZwpxPXN9ZWxzZXtzLnRvU3RyaW5nCnE9cy5jcmVhdGVFbGVtZW50KGEudGFnTmFtZSkKJC54by5ib2R5
+LmFwcGVuZENoaWxkKHEpfWlmKCJjcmVhdGVDb250ZXh0dWFsRnJhZ21lbnQiIGluIHdpbmRvdy5SYW5n
+ZS5wcm90b3R5cGUmJiFDLk5tLnRnKEMuU3EsYS50YWdOYW1lKSl7JC5CTy5zZWxlY3ROb2RlQ29udGVu
+dHMocSkKcz0kLkJPCnMudG9TdHJpbmcKcD1zLmNyZWF0ZUNvbnRleHR1YWxGcmFnbWVudChiPT1udWxs
+PyJudWxsIjpiKX1lbHNle0oud2YocSxiKQpwPSQueG8uY3JlYXRlRG9jdW1lbnRGcmFnbWVudCgpCmZv
+cig7cz1xLmZpcnN0Q2hpbGQscyE9bnVsbDspcC5hcHBlbmRDaGlsZChzKX1pZihxIT09JC54by5ib2R5
+KUouTHQocSkKYy5QbihwKQpkb2N1bWVudC5hZG9wdE5vZGUocCkKcmV0dXJuIHB9LApBSDpmdW5jdGlv
+bihhLGIsYyl7cmV0dXJuIHRoaXMucjYoYSxiLGMsbnVsbCl9LApzaGY6ZnVuY3Rpb24oYSxiKXt0aGlz
+LllDKGEsYil9LApwazpmdW5jdGlvbihhLGIsYyl7dGhpcy5zYTQoYSxudWxsKQphLmFwcGVuZENoaWxk
+KHRoaXMucjYoYSxiLG51bGwsYykpfSwKWUM6ZnVuY3Rpb24oYSxiKXtyZXR1cm4gdGhpcy5wayhhLGIs
+bnVsbCl9LApzUk46ZnVuY3Rpb24oYSxiKXthLmlubmVySFRNTD1ifSwKZ25zOmZ1bmN0aW9uKGEpe3Jl
+dHVybiBhLnRhZ05hbWV9LApnVmw6ZnVuY3Rpb24oYSl7cmV0dXJuIG5ldyBXLkNxKGEsImNsaWNrIiwh
+MSx0LlEpfSwKJGljdjoxfQpXLkN2LnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3JldHVybiB0Lmgu
+Yih0LkEuYShhKSl9LAokUzoyNX0KVy5lYS5wcm90b3R5cGU9eyRpZWE6MX0KVy5EMC5wcm90b3R5cGU9
+ewpPbjpmdW5jdGlvbihhLGIsYyxkKXt0LmJ3LmEoYykKaWYoYyE9bnVsbCl0aGlzLnYoYSxiLGMsZCl9
+LApCOmZ1bmN0aW9uKGEsYixjKXtyZXR1cm4gdGhpcy5PbihhLGIsYyxudWxsKX0sCnY6ZnVuY3Rpb24o
+YSxiLGMsZCl7cmV0dXJuIGEuYWRkRXZlbnRMaXN0ZW5lcihiLEgudFIodC5idy5hKGMpLDEpLGQpfSwK
+JGlEMDoxfQpXLmhILnByb3RvdHlwZT17JGloSDoxfQpXLmg0LnByb3RvdHlwZT17CmdBOmZ1bmN0aW9u
+KGEpe3JldHVybiBhLmxlbmd0aH19ClcuYnIucHJvdG90eXBlPXsKZ0E6ZnVuY3Rpb24oYSl7cmV0dXJu
+IGEubGVuZ3RofX0KVy5WYi5wcm90b3R5cGU9ewpzR1M6ZnVuY3Rpb24oYSxiKXthLmJvZHk9Yn19Clcu
+ZkoucHJvdG90eXBlPXsKZW86ZnVuY3Rpb24oYSxiLGMsZCl7cmV0dXJuIGEub3BlbihiLGMsITApfSwK
+JGlmSjoxfQpXLndhLnByb3RvdHlwZT17fQpXLlNnLnByb3RvdHlwZT17JGlTZzoxfQpXLnU4LnByb3Rv
+dHlwZT17CmdEcjpmdW5jdGlvbihhKXtpZigib3JpZ2luIiBpbiBhKXJldHVybiBhLm9yaWdpbgpyZXR1
+cm4gYS5wcm90b2NvbCsiLy8iK2EuaG9zdH0sCnc6ZnVuY3Rpb24oYSl7cmV0dXJuIFN0cmluZyhhKX0s
+CiRpdTg6MX0KVy5Bai5wcm90b3R5cGU9eyRpQWo6MX0KVy5lNy5wcm90b3R5cGU9ewpncjg6ZnVuY3Rp
+b24oYSl7dmFyIHM9dGhpcy5hLHI9cy5jaGlsZE5vZGVzLmxlbmd0aAppZihyPT09MCl0aHJvdyBILmIo
+UC5QVigiTm8gZWxlbWVudHMiKSkKaWYocj4xKXRocm93IEguYihQLlBWKCJNb3JlIHRoYW4gb25lIGVs
+ZW1lbnQiKSkKcz1zLmZpcnN0Q2hpbGQKcy50b1N0cmluZwpyZXR1cm4gc30sCkZWOmZ1bmN0aW9uKGEs
+Yil7dmFyIHMscixxLHAsbwp0LmVoLmEoYikKaWYoYiBpbnN0YW5jZW9mIFcuZTcpe3M9Yi5hCnI9dGhp
+cy5hCmlmKHMhPT1yKWZvcihxPXMuY2hpbGROb2Rlcy5sZW5ndGgscD0wO3A8cTsrK3Ape289cy5maXJz
+dENoaWxkCm8udG9TdHJpbmcKci5hcHBlbmRDaGlsZChvKX1yZXR1cm59Zm9yKHM9Yi5nbShiKSxyPXRo
+aXMuYTtzLkYoKTspci5hcHBlbmRDaGlsZChzLmdsKCkpfSwKWTU6ZnVuY3Rpb24oYSxiLGMpe3ZhciBz
+LHIKdC5BLmEoYykKcz10aGlzLmEKcj1zLmNoaWxkTm9kZXMKaWYoYjwwfHxiPj1yLmxlbmd0aClyZXR1
+cm4gSC5PSChyLGIpCnMucmVwbGFjZUNoaWxkKGMscltiXSl9LApnbTpmdW5jdGlvbihhKXt2YXIgcz10
+aGlzLmEuY2hpbGROb2RlcwpyZXR1cm4gbmV3IFcuVzkocyxzLmxlbmd0aCxILnpLKHMpLkMoIlc5PEdt
+LkU+IikpfSwKZ0E6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuYS5jaGlsZE5vZGVzLmxlbmd0aH0sCnE6
+ZnVuY3Rpb24oYSxiKXt2YXIgcwpILklaKGIpCnM9dGhpcy5hLmNoaWxkTm9kZXMKaWYoYjwwfHxiPj1z
+Lmxlbmd0aClyZXR1cm4gSC5PSChzLGIpCnJldHVybiBzW2JdfX0KVy5LVi5wcm90b3R5cGU9ewp3Zzpm
+dW5jdGlvbihhKXt2YXIgcz1hLnBhcmVudE5vZGUKaWYocyE9bnVsbClzLnJlbW92ZUNoaWxkKGEpfSwK
+RDQ6ZnVuY3Rpb24oYSl7dmFyIHMKZm9yKDtzPWEuZmlyc3RDaGlsZCxzIT1udWxsOylhLnJlbW92ZUNo
+aWxkKHMpfSwKdzpmdW5jdGlvbihhKXt2YXIgcz1hLm5vZGVWYWx1ZQpyZXR1cm4gcz09bnVsbD90aGlz
+LlUoYSk6c30sCnNhNDpmdW5jdGlvbihhLGIpe2EudGV4dENvbnRlbnQ9Yn0sCm1LOmZ1bmN0aW9uKGEs
+YixjKXtyZXR1cm4gYS5pbnNlcnRCZWZvcmUoYixjKX0sCiRpS1Y6MX0KVy5CSC5wcm90b3R5cGU9ewpn
+QTpmdW5jdGlvbihhKXtyZXR1cm4gYS5sZW5ndGh9LApxOmZ1bmN0aW9uKGEsYil7SC5JWihiKQppZihi
+Pj4+MCE9PWJ8fGI+PWEubGVuZ3RoKXRocm93IEguYihQLkNmKGIsYSxudWxsLG51bGwsbnVsbCkpCnJl
+dHVybiBhW2JdfSwKWTU6ZnVuY3Rpb24oYSxiLGMpe3QuQS5hKGMpCnRocm93IEguYihQLkw0KCJDYW5u
+b3QgYXNzaWduIGVsZW1lbnQgb2YgaW1tdXRhYmxlIExpc3QuIikpfSwKZ3RIOmZ1bmN0aW9uKGEpe2lm
+KGEubGVuZ3RoPjApcmV0dXJuIGFbMF0KdGhyb3cgSC5iKFAuUFYoIk5vIGVsZW1lbnRzIikpfSwKRTpm
+dW5jdGlvbihhLGIpe2lmKGI8MHx8Yj49YS5sZW5ndGgpcmV0dXJuIEguT0goYSxiKQpyZXR1cm4gYVti
+XX0sCiRpYlE6MSwKJGlYajoxLAokaWNYOjEsCiRpek06MX0KVy5TTi5wcm90b3R5cGU9e30KVy5ldy5w
+cm90b3R5cGU9eyRpZXc6MX0KVy5scC5wcm90b3R5cGU9ewpnQTpmdW5jdGlvbihhKXtyZXR1cm4gYS5s
+ZW5ndGh9fQpXLlRiLnByb3RvdHlwZT17CnI2OmZ1bmN0aW9uKGEsYixjLGQpe3ZhciBzLHIKaWYoImNy
+ZWF0ZUNvbnRleHR1YWxGcmFnbWVudCIgaW4gd2luZG93LlJhbmdlLnByb3RvdHlwZSlyZXR1cm4gdGhp
+cy5EVyhhLGIsYyxkKQpzPVcuVTkoIjx0YWJsZT4iK0guRWooYikrIjwvdGFibGU+IixjLGQpCnI9ZG9j
+dW1lbnQuY3JlYXRlRG9jdW1lbnRGcmFnbWVudCgpCm5ldyBXLmU3KHIpLkZWKDAsbmV3IFcuZTcocykp
+CnJldHVybiByfX0KVy5Jdi5wcm90b3R5cGU9ewpyNjpmdW5jdGlvbihhLGIsYyxkKXt2YXIgcyxyCmlm
KCJjcmVhdGVDb250ZXh0dWFsRnJhZ21lbnQiIGluIHdpbmRvdy5SYW5nZS5wcm90b3R5cGUpcmV0dXJu
-IHRoaXMuRFcoYSxiLGMsZCkKcz1XLlU5KCI8dGFibGU+IitILkVqKGIpKyI8L3RhYmxlPiIsYyxkKQpy
-PWRvY3VtZW50LmNyZWF0ZURvY3VtZW50RnJhZ21lbnQoKQpyLnRvU3RyaW5nCnMudG9TdHJpbmcKbmV3
-IFcuZTcocikuRlYoMCxuZXcgVy5lNyhzKSkKcmV0dXJuIHJ9fQpXLkl2LnByb3RvdHlwZT17CnI2OmZ1
-bmN0aW9uKGEsYixjLGQpe3ZhciBzLHIscSxwCmlmKCJjcmVhdGVDb250ZXh0dWFsRnJhZ21lbnQiIGlu
-IHdpbmRvdy5SYW5nZS5wcm90b3R5cGUpcmV0dXJuIHRoaXMuRFcoYSxiLGMsZCkKcz1kb2N1bWVudApy
-PXMuY3JlYXRlRG9jdW1lbnRGcmFnbWVudCgpCnM9Qy5JZS5yNihzLmNyZWF0ZUVsZW1lbnQoInRhYmxl
-IiksYixjLGQpCnMudG9TdHJpbmcKcz1uZXcgVy5lNyhzKQpxPXMuZ3I4KHMpCnEudG9TdHJpbmcKcz1u
-ZXcgVy5lNyhxKQpwPXMuZ3I4KHMpCnIudG9TdHJpbmcKcC50b1N0cmluZwpuZXcgVy5lNyhyKS5GVigw
-LG5ldyBXLmU3KHApKQpyZXR1cm4gcn19ClcuV1AucHJvdG90eXBlPXsKcjY6ZnVuY3Rpb24oYSxiLGMs
-ZCl7dmFyIHMscixxCmlmKCJjcmVhdGVDb250ZXh0dWFsRnJhZ21lbnQiIGluIHdpbmRvdy5SYW5nZS5w
-cm90b3R5cGUpcmV0dXJuIHRoaXMuRFcoYSxiLGMsZCkKcz1kb2N1bWVudApyPXMuY3JlYXRlRG9jdW1l
-bnRGcmFnbWVudCgpCnM9Qy5JZS5yNihzLmNyZWF0ZUVsZW1lbnQoInRhYmxlIiksYixjLGQpCnMudG9T
-dHJpbmcKcz1uZXcgVy5lNyhzKQpxPXMuZ3I4KHMpCnIudG9TdHJpbmcKcS50b1N0cmluZwpuZXcgVy5l
-NyhyKS5GVigwLG5ldyBXLmU3KHEpKQpyZXR1cm4gcn19ClcueVkucHJvdG90eXBlPXsKcGs6ZnVuY3Rp
-b24oYSxiLGMpe3ZhciBzLHIKdGhpcy5zYTQoYSxudWxsKQpzPWEuY29udGVudApzLnRvU3RyaW5nCkou
-YlQocykKcj10aGlzLnI2KGEsYixudWxsLGMpCmEuY29udGVudC5hcHBlbmRDaGlsZChyKX0sCllDOmZ1
-bmN0aW9uKGEsYil7cmV0dXJuIHRoaXMucGsoYSxiLG51bGwpfSwKJGl5WToxfQpXLnc2LnByb3RvdHlw
-ZT17fQpXLks1LnByb3RvdHlwZT17ClBvOmZ1bmN0aW9uKGEsYixjKXt2YXIgcz1XLlAxKGEub3Blbihi
-LGMpKQpyZXR1cm4gc30sCmdtVzpmdW5jdGlvbihhKXtyZXR1cm4gYS5sb2NhdGlvbn0sCnVzOmZ1bmN0
-aW9uKGEsYil7cmV0dXJuIGEuY29uZmlybShiKX0sCiRpSzU6MSwKJGl2NjoxfQpXLkNtLnByb3RvdHlw
-ZT17JGlDbToxfQpXLkNRLnByb3RvdHlwZT17JGlDUToxfQpXLnc0LnByb3RvdHlwZT17Cnc6ZnVuY3Rp
-b24oYSl7dmFyIHMscj1hLmxlZnQKci50b1N0cmluZwpyPSJSZWN0YW5nbGUgKCIrSC5FaihyKSsiLCAi
-CnM9YS50b3AKcy50b1N0cmluZwpzPXIrSC5FaihzKSsiKSAiCnI9YS53aWR0aApyLnRvU3RyaW5nCnI9
-cytILkVqKHIpKyIgeCAiCnM9YS5oZWlnaHQKcy50b1N0cmluZwpyZXR1cm4gcitILkVqKHMpfSwKRE46
-ZnVuY3Rpb24oYSxiKXt2YXIgcyxyCmlmKGI9PW51bGwpcmV0dXJuITEKaWYodC5xLmIoYikpe3M9YS5s
-ZWZ0CnMudG9TdHJpbmcKcj1iLmxlZnQKci50b1N0cmluZwppZihzPT09cil7cz1hLnRvcApzLnRvU3Ry
-aW5nCnI9Yi50b3AKci50b1N0cmluZwppZihzPT09cil7cz1hLndpZHRoCnMudG9TdHJpbmcKcj1iLndp
-ZHRoCnIudG9TdHJpbmcKaWYocz09PXIpe3M9YS5oZWlnaHQKcy50b1N0cmluZwpyPWIuaGVpZ2h0CnIu
-dG9TdHJpbmcKcj1zPT09cgpzPXJ9ZWxzZSBzPSExfWVsc2Ugcz0hMX1lbHNlIHM9ITF9ZWxzZSBzPSEx
-CnJldHVybiBzfSwKZ2lPOmZ1bmN0aW9uKGEpe3ZhciBzLHIscSxwPWEubGVmdApwLnRvU3RyaW5nCnA9
-Qy5DRC5naU8ocCkKcz1hLnRvcApzLnRvU3RyaW5nCnM9Qy5DRC5naU8ocykKcj1hLndpZHRoCnIudG9T
-dHJpbmcKcj1DLkNELmdpTyhyKQpxPWEuaGVpZ2h0CnEudG9TdHJpbmcKcmV0dXJuIFcuckUocCxzLHIs
-Qy5DRC5naU8ocSkpfX0KVy5yaC5wcm90b3R5cGU9ewpnQTpmdW5jdGlvbihhKXtyZXR1cm4gYS5sZW5n
-dGh9LApxOmZ1bmN0aW9uKGEsYil7SC51UChiKQppZihiPj4+MCE9PWJ8fGI+PWEubGVuZ3RoKXRocm93
-IEguYihQLkNmKGIsYSxudWxsLG51bGwsbnVsbCkpCnJldHVybiBhW2JdfSwKWTU6ZnVuY3Rpb24oYSxi
-LGMpe3QuQS5hKGMpCnRocm93IEguYihQLkw0KCJDYW5ub3QgYXNzaWduIGVsZW1lbnQgb2YgaW1tdXRh
-YmxlIExpc3QuIikpfSwKRTpmdW5jdGlvbihhLGIpe2lmKGI8MHx8Yj49YS5sZW5ndGgpcmV0dXJuIEgu
-T0goYSxiKQpyZXR1cm4gYVtiXX0sCiRpYlE6MSwKJGlYajoxLAokaWNYOjEsCiRpek06MX0KVy5jZi5w
-cm90b3R5cGU9ewpLOmZ1bmN0aW9uKGEsYil7dmFyIHMscixxLHAsbwp0LmVBLmEoYikKZm9yKHM9dGhp
-cy5ndmMoKSxyPXMubGVuZ3RoLHE9dGhpcy5hLHA9MDtwPHMubGVuZ3RoO3MubGVuZ3RoPT09cnx8KDAs
-SC5saykocyksKytwKXtvPXNbcF0KYi4kMihvLHEuZ2V0QXR0cmlidXRlKG8pKX19LApndmM6ZnVuY3Rp
-b24oKXt2YXIgcyxyLHEscCxvLG4sbT10aGlzLmEuYXR0cmlidXRlcwptLnRvU3RyaW5nCnM9SC5RSShb
-XSx0LnMpCmZvcihyPW0ubGVuZ3RoLHE9dC5oOSxwPTA7cDxyOysrcCl7aWYocD49bS5sZW5ndGgpcmV0
-dXJuIEguT0gobSxwKQpvPXEuYShtW3BdKQppZihvLm5hbWVzcGFjZVVSST09bnVsbCl7bj1vLm5hbWUK
-bi50b1N0cmluZwpDLk5tLmkocyxuKX19cmV0dXJuIHN9LApnbDA6ZnVuY3Rpb24oYSl7cmV0dXJuIHRo
-aXMuZ3ZjKCkubGVuZ3RoPT09MH19ClcuaTcucHJvdG90eXBlPXsKeDQ6ZnVuY3Rpb24oYSl7dmFyIHM9
-SC5vVCh0aGlzLmEuaGFzQXR0cmlidXRlKGEpKQpyZXR1cm4gc30sCnE6ZnVuY3Rpb24oYSxiKXtyZXR1
-cm4gdGhpcy5hLmdldEF0dHJpYnV0ZShILmgoYikpfSwKWTU6ZnVuY3Rpb24oYSxiLGMpe3RoaXMuYS5z
-ZXRBdHRyaWJ1dGUoYixjKX0sCmdBOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmd2YygpLmxlbmd0aH19
-ClcuU3kucHJvdG90eXBlPXsKeDQ6ZnVuY3Rpb24oYSl7dmFyIHM9SC5vVCh0aGlzLmEuYS5oYXNBdHRy
-aWJ1dGUoImRhdGEtIit0aGlzLk9VKGEpKSkKcmV0dXJuIHN9LApxOmZ1bmN0aW9uKGEsYil7cmV0dXJu
-IHRoaXMuYS5hLmdldEF0dHJpYnV0ZSgiZGF0YS0iK3RoaXMuT1UoSC5oKGIpKSl9LApZNTpmdW5jdGlv
-bihhLGIsYyl7dGhpcy5hLmEuc2V0QXR0cmlidXRlKCJkYXRhLSIrdGhpcy5PVShiKSxjKX0sCks6ZnVu
-Y3Rpb24oYSxiKXt0aGlzLmEuSygwLG5ldyBXLktTKHRoaXMsdC5lQS5hKGIpKSl9LApndmM6ZnVuY3Rp
-b24oKXt2YXIgcz1ILlFJKFtdLHQucykKdGhpcy5hLksoMCxuZXcgVy5BMyh0aGlzLHMpKQpyZXR1cm4g
-c30sCmdBOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmd2YygpLmxlbmd0aH0sCmdsMDpmdW5jdGlvbihh
-KXtyZXR1cm4gdGhpcy5ndmMoKS5sZW5ndGg9PT0wfSwKbjpmdW5jdGlvbihhKXt2YXIgcyxyLHE9SC5R
-SShhLnNwbGl0KCItIiksdC5zKQpmb3Iocz0xO3M8cS5sZW5ndGg7KytzKXtyPXFbc10KaWYoci5sZW5n
-dGg+MClDLk5tLlk1KHEscyxyWzBdLnRvVXBwZXJDYXNlKCkrSi5LVihyLDEpKX1yZXR1cm4gQy5ObS5r
-KHEsIiIpfSwKT1U6ZnVuY3Rpb24oYSl7dmFyIHMscixxLHAsbwpmb3Iocz1hLmxlbmd0aCxyPTAscT0i
-IjtyPHM7KytyKXtwPWFbcl0Kbz1wLnRvTG93ZXJDYXNlKCkKcT0ocCE9PW8mJnI+MD9xKyItIjpxKStv
-fXJldHVybiBxLmNoYXJDb2RlQXQoMCk9PTA/cTpxfX0KVy5LUy5wcm90b3R5cGU9ewokMjpmdW5jdGlv
-bihhLGIpe2lmKEouYXUoYSwiZGF0YS0iKSl0aGlzLmIuJDIodGhpcy5hLm4oQy54Qi55bihhLDUpKSxi
-KX0sCiRTOjE1fQpXLkEzLnByb3RvdHlwZT17CiQyOmZ1bmN0aW9uKGEsYil7aWYoSi5hdShhLCJkYXRh
-LSIpKUMuTm0uaSh0aGlzLmIsdGhpcy5hLm4oQy54Qi55bihhLDUpKSl9LAokUzoxNX0KVy5JNC5wcm90
-b3R5cGU9ewpEOmZ1bmN0aW9uKCl7dmFyIHMscixxLHAsbz1QLkxzKHQuTikKZm9yKHM9dGhpcy5hLmNs
-YXNzTmFtZS5zcGxpdCgiICIpLHI9cy5sZW5ndGgscT0wO3E8cjsrK3Epe3A9Si5UMChzW3FdKQppZihw
-Lmxlbmd0aCE9PTApby5pKDAscCl9cmV0dXJuIG99LApYOmZ1bmN0aW9uKGEpe3RoaXMuYS5jbGFzc05h
-bWU9dC5DLmEoYSkuaygwLCIgIil9LApnQTpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5hLmNsYXNzTGlz
-dC5sZW5ndGh9LApnbDA6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuYS5jbGFzc0xpc3QubGVuZ3RoPT09
-MH0sCmdvcjpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5hLmNsYXNzTGlzdC5sZW5ndGghPT0wfSwKVjE6
-ZnVuY3Rpb24oYSl7dGhpcy5hLmNsYXNzTmFtZT0iIn0sCnRnOmZ1bmN0aW9uKGEsYil7dmFyIHM9dGhp
-cy5hLmNsYXNzTGlzdC5jb250YWlucyhiKQpyZXR1cm4gc30sCmk6ZnVuY3Rpb24oYSxiKXt2YXIgcyxy
-CkguaChiKQpzPXRoaXMuYS5jbGFzc0xpc3QKcj1zLmNvbnRhaW5zKGIpCnMuYWRkKGIpCnJldHVybiFy
-fSwKUjpmdW5jdGlvbihhLGIpe3ZhciBzLHIscQppZih0eXBlb2YgYj09InN0cmluZyIpe3M9dGhpcy5h
-LmNsYXNzTGlzdApyPXMuY29udGFpbnMoYikKcy5yZW1vdmUoYikKcT1yfWVsc2UgcT0hMQpyZXR1cm4g
-cX0sCkZWOmZ1bmN0aW9uKGEsYil7Vy5UTih0aGlzLmEsdC5RLmEoYikpfX0KVy5Gay5wcm90b3R5cGU9
-e30KVy5STy5wcm90b3R5cGU9e30KVy5ldS5wcm90b3R5cGU9e30KVy54Qy5wcm90b3R5cGU9e30KVy52
-Ti5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5hLiQxKHQuQi5hKGEpKX0sCiRT
-OjI3fQpXLkpRLnByb3RvdHlwZT17CkNZOmZ1bmN0aW9uKGEpe3ZhciBzCmlmKCQub3IuYT09PTApe2Zv
-cihzPTA7czwyNjI7KytzKSQub3IuWTUoMCxDLmNtW3NdLFcucFMoKSkKZm9yKHM9MDtzPDEyOysrcykk
-Lm9yLlk1KDAsQy5CSVtzXSxXLlY0KCkpfX0sCmkwOmZ1bmN0aW9uKGEpe3JldHVybiAkLkFOKCkudGco
-MCxXLnJTKGEpKX0sCkViOmZ1bmN0aW9uKGEsYixjKXt2YXIgcz0kLm9yLnEoMCxILkVqKFcuclMoYSkp
-KyI6OiIrYikKaWYocz09bnVsbClzPSQub3IucSgwLCIqOjoiK2IpCmlmKHM9PW51bGwpcmV0dXJuITEK
-cmV0dXJuIEgueTgocy4kNChhLGIsYyx0aGlzKSl9LAokaWtGOjF9ClcuR20ucHJvdG90eXBlPXsKZ206
-ZnVuY3Rpb24oYSl7cmV0dXJuIG5ldyBXLlc5KGEsdGhpcy5nQShhKSxILnpLKGEpLkMoIlc5PEdtLkU+
-IikpfX0KVy52RC5wcm90b3R5cGU9ewppMDpmdW5jdGlvbihhKXtyZXR1cm4gQy5ObS5Wcih0aGlzLmEs
-bmV3IFcuVXYoYSkpfSwKRWI6ZnVuY3Rpb24oYSxiLGMpe3JldHVybiBDLk5tLlZyKHRoaXMuYSxuZXcg
-Vy5FZyhhLGIsYykpfSwKJGlrRjoxfQpXLlV2LnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3JldHVy
-biB0LkUuYShhKS5pMCh0aGlzLmEpfSwKJFM6MTZ9ClcuRWcucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24o
-YSl7cmV0dXJuIHQuRS5hKGEpLkViKHRoaXMuYSx0aGlzLmIsdGhpcy5jKX0sCiRTOjE2fQpXLm02LnBy
-b3RvdHlwZT17CkNZOmZ1bmN0aW9uKGEsYixjLGQpe3ZhciBzLHIscQp0aGlzLmEuRlYoMCxjKQpzPWIu
-ZXYoMCxuZXcgVy5FbygpKQpyPWIuZXYoMCxuZXcgVy5XaygpKQp0aGlzLmIuRlYoMCxzKQpxPXRoaXMu
-YwpxLkZWKDAsQy54RCkKcS5GVigwLHIpfSwKaTA6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuYS50Zygw
-LFcuclMoYSkpfSwKRWI6ZnVuY3Rpb24oYSxiLGMpe3ZhciBzPXRoaXMscj1XLnJTKGEpLHE9cy5jCmlm
-KHEudGcoMCxILkVqKHIpKyI6OiIrYikpcmV0dXJuIHMuZC5EdChjKQplbHNlIGlmKHEudGcoMCwiKjo6
-IitiKSlyZXR1cm4gcy5kLkR0KGMpCmVsc2V7cT1zLmIKaWYocS50ZygwLEguRWoocikrIjo6IitiKSly
-ZXR1cm4hMAplbHNlIGlmKHEudGcoMCwiKjo6IitiKSlyZXR1cm4hMAplbHNlIGlmKHEudGcoMCxILkVq
-KHIpKyI6OioiKSlyZXR1cm4hMAplbHNlIGlmKHEudGcoMCwiKjo6KiIpKXJldHVybiEwfXJldHVybiEx
-fSwKJGlrRjoxfQpXLkVvLnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3JldHVybiFDLk5tLnRnKEMu
-QkksSC5oKGEpKX0sCiRTOjd9ClcuV2sucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7cmV0dXJuIEMu
-Tm0udGcoQy5CSSxILmgoYSkpfSwKJFM6N30KVy5jdC5wcm90b3R5cGU9ewpFYjpmdW5jdGlvbihhLGIs
-Yyl7aWYodGhpcy5qRihhLGIsYykpcmV0dXJuITAKaWYoYj09PSJ0ZW1wbGF0ZSImJmM9PT0iIilyZXR1
-cm4hMAppZihhLmdldEF0dHJpYnV0ZSgidGVtcGxhdGUiKT09PSIiKXJldHVybiB0aGlzLmUudGcoMCxi
-KQpyZXR1cm4hMX19ClcuSUEucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7cmV0dXJuIlRFTVBMQVRF
-OjoiK0guRWooSC5oKGEpKX0sCiRTOjV9ClcuT3cucHJvdG90eXBlPXsKaTA6ZnVuY3Rpb24oYSl7dmFy
-IHMKaWYodC5ldy5iKGEpKXJldHVybiExCnM9dC5nNy5iKGEpCmlmKHMmJlcuclMoYSk9PT0iZm9yZWln
-bk9iamVjdCIpcmV0dXJuITEKaWYocylyZXR1cm4hMApyZXR1cm4hMX0sCkViOmZ1bmN0aW9uKGEsYixj
-KXtpZihiPT09ImlzInx8Qy54Qi5uQyhiLCJvbiIpKXJldHVybiExCnJldHVybiB0aGlzLmkwKGEpfSwK
-JGlrRjoxfQpXLlc5LnByb3RvdHlwZT17CkY6ZnVuY3Rpb24oKXt2YXIgcz10aGlzLHI9cy5jKzEscT1z
-LmIKaWYocjxxKXtzLnNwKEoueDkocy5hLHIpKQpzLmM9cgpyZXR1cm4hMH1zLnNwKG51bGwpCnMuYz1x
-CnJldHVybiExfSwKZ2w6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5kfSwKc3A6ZnVuY3Rpb24oYSl7dGhp
-cy5kPXRoaXMuJHRpLkMoIjE/IikuYShhKX0sCiRpQW46MX0KVy5kVy5wcm90b3R5cGU9eyRpRDA6MSwk
-aXY2OjF9ClcubWsucHJvdG90eXBlPXskaXkwOjF9ClcuS28ucHJvdG90eXBlPXsKUG46ZnVuY3Rpb24o
-YSl7dmFyIHMscj1uZXcgVy5mbSh0aGlzKQpkb3tzPXRoaXMuYgpyLiQyKGEsbnVsbCl9d2hpbGUocyE9
-PXRoaXMuYil9LApFUDpmdW5jdGlvbihhLGIpeysrdGhpcy5iCmlmKGI9PW51bGx8fGIhPT1hLnBhcmVu
-dE5vZGUpSi5MdChhKQplbHNlIGIucmVtb3ZlQ2hpbGQoYSl9LApJNDpmdW5jdGlvbihhLGIpe3ZhciBz
-LHIscSxwLG8sbj0hMCxtPW51bGwsbD1udWxsCnRyeXttPUouaWcoYSkKbD1tLmEuZ2V0QXR0cmlidXRl
-KCJpcyIpCnQuaC5hKGEpCnM9ZnVuY3Rpb24oYyl7aWYoIShjLmF0dHJpYnV0ZXMgaW5zdGFuY2VvZiBO
-YW1lZE5vZGVNYXApKXJldHVybiB0cnVlCmlmKGMuaWQ9PSJsYXN0Q2hpbGQifHxjLm5hbWU9PSJsYXN0
-Q2hpbGQifHxjLmlkPT0icHJldmlvdXNTaWJsaW5nInx8Yy5uYW1lPT0icHJldmlvdXNTaWJsaW5nInx8
-Yy5pZD09ImNoaWxkcmVuInx8Yy5uYW1lPT0iY2hpbGRyZW4iKXJldHVybiB0cnVlCnZhciBrPWMuY2hp
-bGROb2RlcwppZihjLmxhc3RDaGlsZCYmYy5sYXN0Q2hpbGQhPT1rW2subGVuZ3RoLTFdKXJldHVybiB0
-cnVlCmlmKGMuY2hpbGRyZW4paWYoIShjLmNoaWxkcmVuIGluc3RhbmNlb2YgSFRNTENvbGxlY3Rpb258
-fGMuY2hpbGRyZW4gaW5zdGFuY2VvZiBOb2RlTGlzdCkpcmV0dXJuIHRydWUKdmFyIGo9MAppZihjLmNo
-aWxkcmVuKWo9Yy5jaGlsZHJlbi5sZW5ndGgKZm9yKHZhciBpPTA7aTxqO2krKyl7dmFyIGg9Yy5jaGls
-ZHJlbltpXQppZihoLmlkPT0iYXR0cmlidXRlcyJ8fGgubmFtZT09ImF0dHJpYnV0ZXMifHxoLmlkPT0i
-bGFzdENoaWxkInx8aC5uYW1lPT0ibGFzdENoaWxkInx8aC5pZD09InByZXZpb3VzU2libGluZyJ8fGgu
-bmFtZT09InByZXZpb3VzU2libGluZyJ8fGguaWQ9PSJjaGlsZHJlbiJ8fGgubmFtZT09ImNoaWxkcmVu
-IilyZXR1cm4gdHJ1ZX1yZXR1cm4gZmFsc2V9KGEpCm49SC5vVChzKT8hMDohKGEuYXR0cmlidXRlcyBp
-bnN0YW5jZW9mIE5hbWVkTm9kZU1hcCl9Y2F0Y2gocCl7SC5SdShwKX1yPSJlbGVtZW50IHVucHJpbnRh
-YmxlIgp0cnl7cj1KLncoYSl9Y2F0Y2gocCl7SC5SdShwKX10cnl7cT1XLnJTKGEpCnRoaXMua1IodC5o
-LmEoYSksYixuLHIscSx0LmYuYShtKSxILmsobCkpfWNhdGNoKHApe2lmKEguUnUocCkgaW5zdGFuY2Vv
-ZiBQLmMpdGhyb3cgcAplbHNle3RoaXMuRVAoYSxiKQp3aW5kb3cKbz0iUmVtb3ZpbmcgY29ycnVwdGVk
-IGVsZW1lbnQgIitILkVqKHIpCmlmKHR5cGVvZiBjb25zb2xlIT0idW5kZWZpbmVkIil3aW5kb3cuY29u
-c29sZS53YXJuKG8pfX19LAprUjpmdW5jdGlvbihhLGIsYyxkLGUsZixnKXt2YXIgcyxyLHEscCxvLG4s
-bT10aGlzCmlmKGMpe20uRVAoYSxiKQp3aW5kb3cKcz0iUmVtb3ZpbmcgZWxlbWVudCBkdWUgdG8gY29y
-cnVwdGVkIGF0dHJpYnV0ZXMgb24gPCIrZCsiPiIKaWYodHlwZW9mIGNvbnNvbGUhPSJ1bmRlZmluZWQi
-KXdpbmRvdy5jb25zb2xlLndhcm4ocykKcmV0dXJufWlmKCFtLmEuaTAoYSkpe20uRVAoYSxiKQp3aW5k
-b3cKcz0iUmVtb3ZpbmcgZGlzYWxsb3dlZCBlbGVtZW50IDwiK0guRWooZSkrIj4gZnJvbSAiK0guRWoo
-YikKaWYodHlwZW9mIGNvbnNvbGUhPSJ1bmRlZmluZWQiKXdpbmRvdy5jb25zb2xlLndhcm4ocykKcmV0
-dXJufWlmKGchPW51bGwpaWYoIW0uYS5FYihhLCJpcyIsZykpe20uRVAoYSxiKQp3aW5kb3cKcz0iUmVt
-b3ZpbmcgZGlzYWxsb3dlZCB0eXBlIGV4dGVuc2lvbiA8IitILkVqKGUpKycgaXM9IicrZysnIj4nCmlm
-KHR5cGVvZiBjb25zb2xlIT0idW5kZWZpbmVkIil3aW5kb3cuY29uc29sZS53YXJuKHMpCnJldHVybn1z
-PWYuZ3ZjKCkKcj1ILlFJKHMuc2xpY2UoMCksSC50NihzKSkKZm9yKHE9Zi5ndmMoKS5sZW5ndGgtMSxz
-PWYuYTtxPj0wOy0tcSl7aWYocT49ci5sZW5ndGgpcmV0dXJuIEguT0gocixxKQpwPXJbcV0Kbz1tLmEK
-bj1KLmNIKHApCkguaChwKQppZighby5FYihhLG4scy5nZXRBdHRyaWJ1dGUocCkpKXt3aW5kb3cKbz0i
-UmVtb3ZpbmcgZGlzYWxsb3dlZCBhdHRyaWJ1dGUgPCIrSC5FaihlKSsiICIrcCsnPSInK0guRWoocy5n
-ZXRBdHRyaWJ1dGUocCkpKyciPicKaWYodHlwZW9mIGNvbnNvbGUhPSJ1bmRlZmluZWQiKXdpbmRvdy5j
-b25zb2xlLndhcm4obykKcy5yZW1vdmVBdHRyaWJ1dGUocCl9fWlmKHQuYVcuYihhKSl7cz1hLmNvbnRl
-bnQKcy50b1N0cmluZwptLlBuKHMpfX0sCiRpb246MX0KVy5mbS5wcm90b3R5cGU9ewokMjpmdW5jdGlv
-bihhLGIpe3ZhciBzLHIscSxwLG8sbixtPXRoaXMuYQpzd2l0Y2goYS5ub2RlVHlwZSl7Y2FzZSAxOm0u
-STQoYSxiKQpicmVhawpjYXNlIDg6Y2FzZSAxMTpjYXNlIDM6Y2FzZSA0OmJyZWFrCmRlZmF1bHQ6bS5F
-UChhLGIpfXM9YS5sYXN0Q2hpbGQKZm9yKHE9dC5BO3MhPW51bGw7KXtyPW51bGwKdHJ5e3I9cy5wcmV2
-aW91c1NpYmxpbmcKaWYociE9bnVsbCl7cD1yLm5leHRTaWJsaW5nCm89cwpvPXA9PW51bGw/byE9bnVs
-bDpwIT09bwpwPW99ZWxzZSBwPSExCmlmKHApe3A9UC5QVigiQ29ycnVwdCBIVE1MIikKdGhyb3cgSC5i
-KHApfX1jYXRjaChuKXtILlJ1KG4pCnA9cS5hKHMpOysrbS5iCm89cC5wYXJlbnROb2RlCm89YT09bnVs
-bD9vIT1udWxsOmEhPT1vCmlmKG8pe289cC5wYXJlbnROb2RlCmlmKG8hPW51bGwpby5yZW1vdmVDaGls
-ZChwKX1lbHNlIGEucmVtb3ZlQ2hpbGQocCkKcz1udWxsCnI9YS5sYXN0Q2hpbGR9aWYocyE9bnVsbCl0
-aGlzLiQyKHMsYSkKcz1yfX0sCiRTOjMwfQpXLkxlLnByb3RvdHlwZT17fQpXLks3LnByb3RvdHlwZT17
-fQpXLnJCLnByb3RvdHlwZT17fQpXLlhXLnByb3RvdHlwZT17fQpXLm9hLnByb3RvdHlwZT17fQpQLmlK
-LnByb3RvdHlwZT17ClZIOmZ1bmN0aW9uKGEpe3ZhciBzLHI9dGhpcy5hLHE9ci5sZW5ndGgKZm9yKHM9
-MDtzPHE7KytzKWlmKHJbc109PT1hKXJldHVybiBzCkMuTm0uaShyLGEpCkMuTm0uaSh0aGlzLmIsbnVs
-bCkKcmV0dXJuIHF9LApQdjpmdW5jdGlvbihhKXt2YXIgcyxyLHEscD10aGlzLG89e30KaWYoYT09bnVs
-bClyZXR1cm4gYQppZihILnJRKGEpKXJldHVybiBhCmlmKHR5cGVvZiBhPT0ibnVtYmVyIilyZXR1cm4g
-YQppZih0eXBlb2YgYT09InN0cmluZyIpcmV0dXJuIGEKaWYoYSBpbnN0YW5jZW9mIFAuaVApcmV0dXJu
-IG5ldyBEYXRlKGEuYSkKaWYodC5mdi5iKGEpKXRocm93IEguYihQLlNZKCJzdHJ1Y3R1cmVkIGNsb25l
-IG9mIFJlZ0V4cCIpKQppZih0LmM4LmIoYSkpcmV0dXJuIGEKaWYodC53LmIoYSkpcmV0dXJuIGEKaWYo
-dC5JLmIoYSkpcmV0dXJuIGEKcz10LmRFLmIoYSl8fCExCmlmKHMpcmV0dXJuIGEKaWYodC5mLmIoYSkp
-e3I9cC5WSChhKQpzPXAuYgppZihyPj1zLmxlbmd0aClyZXR1cm4gSC5PSChzLHIpCnE9by5hPXNbcl0K
-aWYocSE9bnVsbClyZXR1cm4gcQpxPXt9Cm8uYT1xCkMuTm0uWTUocyxyLHEpCmEuSygwLG5ldyBQLkUy
-KG8scCkpCnJldHVybiBvLmF9aWYodC5qLmIoYSkpe3I9cC5WSChhKQpvPXAuYgppZihyPj1vLmxlbmd0
-aClyZXR1cm4gSC5PSChvLHIpCnE9b1tyXQppZihxIT1udWxsKXJldHVybiBxCnJldHVybiBwLmVrKGEs
-cil9aWYodC5lSC5iKGEpKXtyPXAuVkgoYSkKcz1wLmIKaWYocj49cy5sZW5ndGgpcmV0dXJuIEguT0go
-cyxyKQpxPW8uYj1zW3JdCmlmKHEhPW51bGwpcmV0dXJuIHEKcT17fQpvLmI9cQpDLk5tLlk1KHMscixx
-KQpwLmltKGEsbmV3IFAuamcobyxwKSkKcmV0dXJuIG8uYn10aHJvdyBILmIoUC5TWSgic3RydWN0dXJl
-ZCBjbG9uZSBvZiBvdGhlciB0eXBlIikpfSwKZWs6ZnVuY3Rpb24oYSxiKXt2YXIgcyxyPUouVTYoYSks
-cT1yLmdBKGEpLHA9bmV3IEFycmF5KHEpCkMuTm0uWTUodGhpcy5iLGIscCkKZm9yKHM9MDtzPHE7Kytz
-KUMuTm0uWTUocCxzLHRoaXMuUHYoci5xKGEscykpKQpyZXR1cm4gcH19ClAuRTIucHJvdG90eXBlPXsK
-JDI6ZnVuY3Rpb24oYSxiKXt0aGlzLmEuYVthXT10aGlzLmIuUHYoYil9LAokUzozMX0KUC5qZy5wcm90
-b3R5cGU9ewokMjpmdW5jdGlvbihhLGIpe3RoaXMuYS5iW2FdPXRoaXMuYi5QdihiKX0sCiRTOjE3fQpQ
-LkJmLnByb3RvdHlwZT17CmltOmZ1bmN0aW9uKGEsYil7dmFyIHMscixxLHAKdC5iOC5hKGIpCmZvcihz
-PU9iamVjdC5rZXlzKGEpLHI9cy5sZW5ndGgscT0wO3E8cjsrK3Epe3A9c1txXQpiLiQyKHAsYVtwXSl9
-fX0KUC5Bcy5wcm90b3R5cGU9ewpWOmZ1bmN0aW9uKGEpe3ZhciBzCkguaChhKQpzPSQuaEcoKS5iCmlm
-KHR5cGVvZiBhIT0ic3RyaW5nIilILnYoSC50TChhKSkKaWYocy50ZXN0KGEpKXJldHVybiBhCnRocm93
-IEguYihQLkwzKGEsInZhbHVlIiwiTm90IGEgdmFsaWQgY2xhc3MgdG9rZW4iKSl9LAp3OmZ1bmN0aW9u
-KGEpe3JldHVybiB0aGlzLkQoKS5rKDAsIiAiKX0sCmdtOmZ1bmN0aW9uKGEpe3ZhciBzPXRoaXMuRCgp
-CnJldHVybiBQLnJqKHMscy5yLEguTGgocykuYyl9LApnbDA6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMu
-RCgpLmE9PT0wfSwKZ29yOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLkQoKS5hIT09MH0sCmdBOmZ1bmN0
-aW9uKGEpe3JldHVybiB0aGlzLkQoKS5hfSwKdGc6ZnVuY3Rpb24oYSxiKXt0aGlzLlYoYikKcmV0dXJu
-IHRoaXMuRCgpLnRnKDAsYil9LAppOmZ1bmN0aW9uKGEsYil7dmFyIHMKSC5oKGIpCnRoaXMuVihiKQpz
-PXRoaXMuT1MobmV3IFAuR0UoYikpCnJldHVybiBILnk4KHM9PW51bGw/ITE6cyl9LApSOmZ1bmN0aW9u
-KGEsYil7dmFyIHMscgppZih0eXBlb2YgYiE9InN0cmluZyIpcmV0dXJuITEKdGhpcy5WKGIpCnM9dGhp
-cy5EKCkKcj1zLlIoMCxiKQp0aGlzLlgocykKcmV0dXJuIHJ9LApGVjpmdW5jdGlvbihhLGIpe3RoaXMu
-T1MobmV3IFAuTjcodGhpcyx0LlEuYShiKSkpfSwKZVI6ZnVuY3Rpb24oYSxiKXt2YXIgcz10aGlzLkQo
-KQpyZXR1cm4gSC5iSyhzLGIsSC5MaChzKS5DKCJsZi5FIikpfSwKRTpmdW5jdGlvbihhLGIpe3JldHVy
-biB0aGlzLkQoKS5FKDAsYil9LApWMTpmdW5jdGlvbihhKXt0aGlzLk9TKG5ldyBQLnVRKCkpfSwKT1M6
-ZnVuY3Rpb24oYSl7dmFyIHMscgp0LmJVLmEoYSkKcz10aGlzLkQoKQpyPWEuJDEocykKdGhpcy5YKHMp
-CnJldHVybiByfX0KUC5HRS5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXtyZXR1cm4gdC5DLmEoYSku
-aSgwLHRoaXMuYSl9LAokUzozM30KUC5ONy5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXt2YXIgcz10
-aGlzLmIscj1ILnQ2KHMpCnJldHVybiB0LkMuYShhKS5GVigwLG5ldyBILmxKKHMsci5DKCJxVSgxKSIp
-LmEodGhpcy5hLmd1TSgpKSxyLkMoImxKPDEscVU+IikpKX0sCiRTOjE4fQpQLnVRLnByb3RvdHlwZT17
-CiQxOmZ1bmN0aW9uKGEpe3QuQy5hKGEpCmlmKGEuYT4wKXthLmI9YS5jPWEuZD1hLmU9YS5mPW51bGwK
-YS5hPTAKYS5TKCl9cmV0dXJuIG51bGx9LAokUzoxOH0KUC5oRi5wcm90b3R5cGU9eyRpaEY6MX0KUC5Q
-Qy5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXt2YXIgcwp0LlkuYShhKQpzPWZ1bmN0aW9uKGIsYyxk
-KXtyZXR1cm4gZnVuY3Rpb24oKXtyZXR1cm4gYihjLGQsdGhpcyxBcnJheS5wcm90b3R5cGUuc2xpY2Uu
-YXBwbHkoYXJndW1lbnRzKSl9fShQLlI0LGEsITEpClAuRG0ocywkLnooKSxhKQpyZXR1cm4gc30sCiRT
-OjR9ClAubXQucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7cmV0dXJuIG5ldyB0aGlzLmEoYSl9LAok
-Uzo0fQpQLlFTLnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3JldHVybiBuZXcgUC5yNyhhKX0sCiRT
-OjM1fQpQLm5wLnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3JldHVybiBuZXcgUC5UeihhLHQuYW0p
-fSwKJFM6NDZ9ClAuVXQucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7cmV0dXJuIG5ldyBQLkU0KGEp
-fSwKJFM6Mzd9ClAuRTQucHJvdG90eXBlPXsKcTpmdW5jdGlvbihhLGIpe2lmKHR5cGVvZiBiIT0ic3Ry
-aW5nIiYmdHlwZW9mIGIhPSJudW1iZXIiKXRocm93IEguYihQLnhZKCJwcm9wZXJ0eSBpcyBub3QgYSBT
-dHJpbmcgb3IgbnVtIikpCnJldHVybiBQLmRVKHRoaXMuYVtiXSl9LApZNTpmdW5jdGlvbihhLGIsYyl7
-aWYodHlwZW9mIGIhPSJzdHJpbmciJiZ0eXBlb2YgYiE9Im51bWJlciIpdGhyb3cgSC5iKFAueFkoInBy
-b3BlcnR5IGlzIG5vdCBhIFN0cmluZyBvciBudW0iKSkKdGhpcy5hW2JdPVAud1koYyl9LApETjpmdW5j
-dGlvbihhLGIpe2lmKGI9PW51bGwpcmV0dXJuITEKcmV0dXJuIGIgaW5zdGFuY2VvZiBQLkU0JiZ0aGlz
-LmE9PT1iLmF9LAp3OmZ1bmN0aW9uKGEpe3ZhciBzLHIKdHJ5e3M9U3RyaW5nKHRoaXMuYSkKcmV0dXJu
-IHN9Y2F0Y2gocil7SC5SdShyKQpzPXRoaXMueGIoMCkKcmV0dXJuIHN9fSwKVjc6ZnVuY3Rpb24oYSxi
-KXt2YXIgcyxyPXRoaXMuYQppZihiPT1udWxsKXM9bnVsbAplbHNle3M9SC50NihiKQpzPVAuQ0gobmV3
-IEgubEooYixzLkMoIkAoMSkiKS5hKFAuaUcoKSkscy5DKCJsSjwxLEA+IikpLCEwLHQueil9cmV0dXJu
-IFAuZFUoclthXS5hcHBseShyLHMpKX0sCmdpTzpmdW5jdGlvbihhKXtyZXR1cm4gMH19ClAucjcucHJv
-dG90eXBlPXt9ClAuVHoucHJvdG90eXBlPXsKY1A6ZnVuY3Rpb24oYSl7dmFyIHM9dGhpcyxyPWE8MHx8
-YT49cy5nQShzKQppZihyKXRocm93IEguYihQLlRFKGEsMCxzLmdBKHMpLG51bGwsbnVsbCkpfSwKcTpm
-dW5jdGlvbihhLGIpe2lmKEgub2soYikpdGhpcy5jUChiKQpyZXR1cm4gdGhpcy4kdGkuYy5hKHRoaXMu
-VXIoMCxiKSl9LApZNTpmdW5jdGlvbihhLGIsYyl7dGhpcy5jUChiKQp0aGlzLmJoKDAsYixjKX0sCmdB
-OmZ1bmN0aW9uKGEpe3ZhciBzPXRoaXMuYS5sZW5ndGgKaWYodHlwZW9mIHM9PT0ibnVtYmVyIiYmcz4+
-PjA9PT1zKXJldHVybiBzCnRocm93IEguYihQLlBWKCJCYWQgSnNBcnJheSBsZW5ndGgiKSl9LAokaWJR
-OjEsCiRpY1g6MSwKJGl6TToxfQpQLnZnLnByb3RvdHlwZT17Clk1OmZ1bmN0aW9uKGEsYixjKXtyZXR1
-cm4gdGhpcy5lNCgwLGIsYyl9fQpQLm5kLnByb3RvdHlwZT17JGluZDoxfQpQLktlLnByb3RvdHlwZT17
-CkQ6ZnVuY3Rpb24oKXt2YXIgcyxyLHEscCxvPXRoaXMuYS5nZXRBdHRyaWJ1dGUoImNsYXNzIiksbj1Q
-LkxzKHQuTikKaWYobz09bnVsbClyZXR1cm4gbgpmb3Iocz1vLnNwbGl0KCIgIikscj1zLmxlbmd0aCxx
-PTA7cTxyOysrcSl7cD1KLlQwKHNbcV0pCmlmKHAubGVuZ3RoIT09MCluLmkoMCxwKX1yZXR1cm4gbn0s
-Clg6ZnVuY3Rpb24oYSl7dGhpcy5hLnNldEF0dHJpYnV0ZSgiY2xhc3MiLGEuaygwLCIgIikpfX0KUC5o
-aS5wcm90b3R5cGU9ewpnUDpmdW5jdGlvbihhKXtyZXR1cm4gbmV3IFAuS2UoYSl9LApzaGY6ZnVuY3Rp
-b24oYSxiKXt0aGlzLllDKGEsYil9LApyNjpmdW5jdGlvbihhLGIsYyxkKXt2YXIgcyxyLHEscCxvLG4K
-aWYoZD09bnVsbCl7cz1ILlFJKFtdLHQudikKZD1uZXcgVy52RChzKQpDLk5tLmkocyxXLlR3KG51bGwp
-KQpDLk5tLmkocyxXLkJsKCkpCkMuTm0uaShzLG5ldyBXLk93KCkpfWM9bmV3IFcuS28oZCkKcj0nPHN2
-ZyB2ZXJzaW9uPSIxLjEiPicrSC5FaihiKSsiPC9zdmc+IgpzPWRvY3VtZW50CnE9cy5ib2R5CnEudG9T
-dHJpbmcKcD1DLlJZLkFIKHEscixjKQpvPXMuY3JlYXRlRG9jdW1lbnRGcmFnbWVudCgpCnAudG9TdHJp
-bmcKcz1uZXcgVy5lNyhwKQpuPXMuZ3I4KHMpCmZvcig7cz1uLmZpcnN0Q2hpbGQscyE9bnVsbDspby5h
-cHBlbmRDaGlsZChzKQpyZXR1cm4gb30sCm56OmZ1bmN0aW9uKGEsYixjLGQsZSl7dGhyb3cgSC5iKFAu
-TDQoIkNhbm5vdCBpbnZva2UgaW5zZXJ0QWRqYWNlbnRIdG1sIG9uIFNWRy4iKSl9LApnVmw6ZnVuY3Rp
-b24oYSl7cmV0dXJuIG5ldyBXLmV1KGEsImNsaWNrIiwhMSx0LmspfSwKJGloaToxfQpVLmQyLnByb3Rv
-dHlwZT17Ckx0OmZ1bmN0aW9uKCl7dmFyIHMscixxLHAsbz10aGlzLG49dC5YLG09dC5fLGw9UC5GbChu
-LG0pLGs9by5hCmlmKGshPW51bGwpe3M9SC5RSShbXSx0LkcpCmZvcihyPWsubGVuZ3RoLHE9MDtxPGsu
-bGVuZ3RoO2subGVuZ3RoPT09cnx8KDAsSC5saykoayksKytxKXtwPWtbcV0Kcy5wdXNoKFAuRUYoWyJk
-ZXNjcmlwdGlvbiIscC5hLCJocmVmIixwLmJdLG4sbSkpfWwuWTUoMCwiZWRpdHMiLHMpfWwuWTUoMCwi
-ZXhwbGFuYXRpb24iLG8uYikKbC5ZNSgwLCJsaW5lIixvLmMpCmwuWTUoMCwiZGlzcGxheVBhdGgiLG8u
-ZCkKbC5ZNSgwLCJ1cmlQYXRoIixvLmUpCm49by5mCmlmKG4hPW51bGwpe209SC5RSShbXSx0LkcpCmZv
-cihrPW4ubGVuZ3RoLHE9MDtxPG4ubGVuZ3RoO24ubGVuZ3RoPT09a3x8KDAsSC5saykobiksKytxKW0u
-cHVzaChuW3FdLkx0KCkpCmwuWTUoMCwidHJhY2VzIixtKX1yZXR1cm4gbH19ClUuU2UucHJvdG90eXBl
-PXsKTHQ6ZnVuY3Rpb24oKXtyZXR1cm4gUC5FRihbImRlc2NyaXB0aW9uIix0aGlzLmEsImhyZWYiLHRo
-aXMuYl0sdC5YLHQuXyl9fQpVLk1sLnByb3RvdHlwZT17Ckx0OmZ1bmN0aW9uKCl7cmV0dXJuIFAuRUYo
-WyJocmVmIix0aGlzLmEsImxpbmUiLHRoaXMuYiwicGF0aCIsdGhpcy5jXSx0LlgsdC5fKX19ClUueUQu
-cHJvdG90eXBlPXsKTHQ6ZnVuY3Rpb24oKXt2YXIgcyxyLHEscD1ILlFJKFtdLHQuRykKZm9yKHM9dGhp
-cy5iLHI9cy5sZW5ndGgscT0wO3E8cy5sZW5ndGg7cy5sZW5ndGg9PT1yfHwoMCxILmxrKShzKSwrK3Ep
-cC5wdXNoKHNbcV0uTHQoKSkKcmV0dXJuIFAuRUYoWyJkZXNjcmlwdGlvbiIsdGhpcy5hLCJlbnRyaWVz
-IixwXSx0LlgsdC5fKX19ClUud2IucHJvdG90eXBlPXsKTHQ6ZnVuY3Rpb24oKXt2YXIgcyxyLHEscD10
-aGlzLG89UC5GbCh0LlgsdC5fKQpvLlk1KDAsImRlc2NyaXB0aW9uIixwLmEpCnM9cC5iCmlmKHMhPW51
-bGwpby5ZNSgwLCJmdW5jdGlvbiIscykKcz1wLmMKaWYocyE9bnVsbClvLlk1KDAsImxpbmsiLHMuTHQo
-KSkKcz1wLmQKaWYocy5sZW5ndGghPT0wKXtyPUgudDYocykKcT1yLkMoImxKPDEsWjA8cVUqLE1oKj4q
-PiIpCm8uWTUoMCwiaGludEFjdGlvbnMiLFAuWTEobmV3IEgubEoocyxyLkMoIlowPHFVKixNaCo+Kigx
-KSIpLmEobmV3IFUuYjAoKSkscSksITAscS5DKCJhTC5FIikpKX1yZXR1cm4gb319ClUuYU4ucHJvdG90
-eXBlPXsKJDE6ZnVuY3Rpb24oYSl7cmV0dXJuIFIubnoodC50LmEoYSkpfSwKJFM6Mzh9ClUuYjAucHJv
-dG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7cmV0dXJuIHQuYVguYShhKS5MdCgpfSwKJFM6Mzl9CkIuajgu
-cHJvdG90eXBlPXsKTHQ6ZnVuY3Rpb24oKXtyZXR1cm4gUC5FRihbImxpbmUiLHRoaXMuYSwiZXhwbGFu
-YXRpb24iLHRoaXMuYiwib2Zmc2V0Iix0aGlzLmNdLHQuWCx0Ll8pfX0KQi5xcC5wcm90b3R5cGU9ewpM
-dDpmdW5jdGlvbigpe3ZhciBzLHIscSxwLG8sbixtLGw9dGhpcyxrPXQuWCxqPVAuRmwoayx0LmRwKQpm
-b3Iocz1sLmQscz1zLmdQdShzKSxzPXMuZ20ocykscj10Ll8scT10Lkc7cy5GKCk7KXtwPXMuZ2woKQpv
-PXAuYQpuPUguUUkoW10scSkKZm9yKHA9Si5JVChwLmIpO3AuRigpOyl7bT1wLmdsKCkKbi5wdXNoKFAu
-RUYoWyJsaW5lIixtLmEsImV4cGxhbmF0aW9uIixtLmIsIm9mZnNldCIsbS5jXSxrLHIpKX1qLlk1KDAs
-byxuKX1yZXR1cm4gUC5FRihbInJlZ2lvbnMiLGwuYSwibmF2aWdhdGlvbkNvbnRlbnQiLGwuYiwic291
-cmNlQ29kZSIsbC5jLCJlZGl0cyIsal0sayxyKX19ClQubVEucHJvdG90eXBlPXt9CkwuZS5wcm90b3R5
-cGU9ewokMTpmdW5jdGlvbihhKXt2YXIgcyxyLHEscCxvLG4sbQp0LmFMLmEoYSkKcz13aW5kb3cubG9j
-YXRpb24ucGF0aG5hbWUKcj1MLkc2KHdpbmRvdy5sb2NhdGlvbi5ocmVmKQpxPUwuYUsod2luZG93Lmxv
-Y2F0aW9uLmhyZWYpCkwuR2UoKQppZihzIT09Ii8iJiZzIT09Si5UMChkb2N1bWVudC5xdWVyeVNlbGVj
-dG9yKCIucm9vdCIpLnRleHRDb250ZW50KSlMLkc3KHMscixxLCEwLG5ldyBMLlZXKHMscixxKSkKcD1k
-b2N1bWVudApvPUoucUYocC5xdWVyeVNlbGVjdG9yKCIuYXBwbHktbWlncmF0aW9uIikpCm49by4kdGkK
-bT1uLkMoIn4oMSk/IikuYShuZXcgTC5vWigpKQp0LlouYShudWxsKQpXLkpFKG8uYSxvLmIsbSwhMSxu
-LmMpCm49Si5xRihwLnF1ZXJ5U2VsZWN0b3IoIi5yZXJ1bi1taWdyYXRpb24iKSkKbT1uLiR0aQpXLkpF
-KG4uYSxuLmIsbS5DKCJ+KDEpPyIpLmEobmV3IEwuSGkoKSksITEsbS5jKQptPUoucUYocC5xdWVyeVNl
-bGVjdG9yKCIucmVwb3J0LXByb2JsZW0iKSkKbj1tLiR0aQpXLkpFKG0uYSxtLmIsbi5DKCJ+KDEpPyIp
-LmEobmV3IEwuQlQoKSksITEsbi5jKQpwPUoucUYocC5xdWVyeVNlbGVjdG9yKCIucG9wdXAtcGFuZSAu
-Y2xvc2UiKSkKbj1wLiR0aQpXLkpFKHAuYSxwLmIsbi5DKCJ+KDEpPyIpLmEobmV3IEwuUFkoKSksITEs
-bi5jKQpuPUoucUYoJC5jMCgpKQpwPW4uJHRpClcuSkUobi5hLG4uYixwLkMoIn4oMSk/IikuYShuZXcg
-TC51OCgpKSwhMSxwLmMpfSwKJFM6MTl9CkwuVlcucHJvdG90eXBlPXsKJDA6ZnVuY3Rpb24oKXtMLkZy
-KHRoaXMuYSx0aGlzLmIsdGhpcy5jKX0sCiRTOjJ9Ckwub1oucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24o
-YSl7dmFyIHMscixxLHAsbwp0Lk8uYShhKQppZihILm9UKEMub2wudXMod2luZG93LCJUaGlzIHdpbGwg
-YXBwbHkgdGhlIGNoYW5nZXMgeW91J3ZlIHByZXZpZXdlZCB0byB5b3VyIHdvcmtpbmcgZGlyZWN0b3J5
-LiBJdCBpcyByZWNvbW1lbmRlZCB5b3UgY29tbWl0IGFueSBjaGFuZ2VzIHlvdSBtYWRlIGJlZm9yZSBk
-b2luZyB0aGlzLiIpKSl7cz1ILlFJKFtdLHQuRykKZm9yKHI9JC5JUixxPXIubGVuZ3RoLHA9MDtwPHIu
-bGVuZ3RoO3IubGVuZ3RoPT09cXx8KDAsSC5saykociksKytwKXMucHVzaChyW3BdLkx0KCkpCnM9TC50
-eSgiL2FwcGx5LW1pZ3JhdGlvbiIsUC5FRihbIm5hdmlnYXRpb25UcmVlIixzXSx0LlgsdC5kcCkpLlc3
-KG5ldyBMLmpyKCksdC5QKQpvPW5ldyBMLnFsKCkKdC5iNy5hKG51bGwpCnI9cy4kdGkKcT0kLlgzCmlm
-KHEhPT1DLk5VKW89UC5WSChvLHEpCnMueGYobmV3IFAuRmUobmV3IFAudnMocSxyKSwyLG51bGwsbyxy
-LkMoIkA8MT4iKS5LcShyLmMpLkMoIkZlPDEsMj4iKSkpfX0sCiRTOjF9CkwuanIucHJvdG90eXBlPXsK
-JDE6ZnVuY3Rpb24oYSl7dmFyIHMKdC50LmEoYSkKcz1kb2N1bWVudC5ib2R5CnMuY2xhc3NMaXN0LnJl
-bW92ZSgicHJvcG9zZWQiKQpzLmNsYXNzTGlzdC5hZGQoImFwcGxpZWQiKX0sCiRTOjQyfQpMLnFsLnBy
-b3RvdHlwZT17CiQyOmZ1bmN0aW9uKGEsYil7TC5DMigiQ291bGRuJ3QgYXBwbHkgbWlncmF0aW9uIixh
-LGIpfSwKJEM6IiQyIiwKJFI6MiwKJFM6MTd9CkwuSGkucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7
-cmV0dXJuIHRoaXMueG4odC5PLmEoYSkpfSwKeG46ZnVuY3Rpb24oYSl7dmFyIHM9MCxyPVAuRlgodC5Q
-KSxxPTEscCxvPVtdLG4sbSxsLGssagp2YXIgJGFzeW5jJCQxPVAubHooZnVuY3Rpb24oYixjKXtpZihi
-PT09MSl7cD1jCnM9cX13aGlsZSh0cnVlKXN3aXRjaChzKXtjYXNlIDA6cT0zCmRvY3VtZW50LmJvZHku
-Y2xhc3NMaXN0LmFkZCgicmVydW5uaW5nIikKcz02CnJldHVybiBQLmpRKEwudHkoIi9yZXJ1bi1taWdy
-YXRpb24iLG51bGwpLCRhc3luYyQkMSkKY2FzZSA2Om49YwppZihILm9UKEgueTgoSi54OShuLCJzdWNj
-ZXNzIikpKSl3aW5kb3cubG9jYXRpb24ucmVsb2FkKCkKZWxzZSBMLkswKHQuZUUuYShKLng5KG4sImVy
-cm9ycyIpKSkKby5wdXNoKDUpCnM9NApicmVhawpjYXNlIDM6cT0yCmo9cAptPUguUnUoaikKbD1ILnRz
-KGopCkwuQzIoIkZhaWxlZCB0byByZXJ1biBtaWdyYXRpb24iLG0sbCkKby5wdXNoKDUpCnM9NApicmVh
-awpjYXNlIDI6bz1bMV0KY2FzZSA0OnE9MQpkb2N1bWVudC5ib2R5LmNsYXNzTGlzdC5yZW1vdmUoInJl
-cnVubmluZyIpCnM9by5wb3AoKQpicmVhawpjYXNlIDU6cmV0dXJuIFAueUMobnVsbCxyKQpjYXNlIDE6
-cmV0dXJuIFAuZjMocCxyKX19KQpyZXR1cm4gUC5ESSgkYXN5bmMkJDEscil9LAokUzoyMH0KTC5CVC5w
-cm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXt0Lk8uYShhKQpDLm9sLlBvKHdpbmRvdyxQLlhkKCJodHRw
-cyIsImdpdGh1Yi5jb20iLCJkYXJ0LWxhbmcvc2RrL2lzc3Vlcy9uZXciLFAuRUYoWyJ0aXRsZSIsIkN1
-c3RvbWVyLXJlcG9ydGVkIGlzc3VlIHdpdGggTk5CRCBtaWdyYXRpb24gdG9vbCIsImxhYmVscyIsdS5k
-LCJib2R5IiwiIyMjIyBTdGVwcyB0byByZXByb2R1Y2VcblxuIyMjIyBXaGF0IGRpZCB5b3UgZXhwZWN0
-IHRvIGhhcHBlbj9cblxuIyMjIyBXaGF0IGFjdHVhbGx5IGhhcHBlbmVkP1xuXG5fU2NyZWVuc2hvdHMg
-YXJlIGFwcHJlY2lhdGVkX1xuXG4qKkRhcnQgU0RLIHZlcnNpb24qKjogIitILkVqKGRvY3VtZW50Lmdl
-dEVsZW1lbnRCeUlkKCJzZGstdmVyc2lvbiIpLnRleHRDb250ZW50KSsiXG5cblRoYW5rcyBmb3IgZmls
-aW5nIVxuIl0sdC5YLHQueikpLmduRCgpLCJyZXBvcnQtcHJvYmxlbSIpfSwKJFM6MX0KTC5QWS5wcm90
-b3R5cGU9ewokMTpmdW5jdGlvbihhKXt2YXIgcwp0Lk8uYShhKQpzPWRvY3VtZW50LnF1ZXJ5U2VsZWN0
-b3IoIi5wb3B1cC1wYW5lIikuc3R5bGUKcy5kaXNwbGF5PSJub25lIgpyZXR1cm4ibm9uZSJ9LAokUzo0
-NH0KTC51OC5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXt2YXIgcyxyLHEscAp0Lk8uYShhKQpzPSQu
-RDkoKS5pbm5lclRleHQKcj10LmcuYShkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCcubmF2LXBhbmVsIFtk
-YXRhLW5hbWUqPSInK0guRWooVy5MaihzKSkrJyJdJykucGFyZW50Tm9kZSkKcT1yLnF1ZXJ5U2VsZWN0
-b3IoIi5zdGF0dXMtaWNvbiIpCnA9TC55dygkLklSLHMpCmlmKHAgaW5zdGFuY2VvZiBMLmNEJiZILm9U
-KHAueCkpe0wuT3QocCkKTC54bihxLHApCkwuQVIocixwKX19LAokUzoxfQpMLkwucHJvdG90eXBlPXsK
-JDE6ZnVuY3Rpb24oYSl7dmFyIHMscixxCnQuYUwuYShhKQpzPXdpbmRvdy5sb2NhdGlvbi5wYXRobmFt
-ZQpyPUwuRzYod2luZG93LmxvY2F0aW9uLmhyZWYpCnE9TC5hSyh3aW5kb3cubG9jYXRpb24uaHJlZikK
-aWYocy5sZW5ndGg+MSlMLkc3KHMscixxLCExLG51bGwpCmVsc2V7TC5CRShzLEIud1IoKSwhMCkKTC5C
-WCgiJm5ic3A7IixudWxsKX19LAokUzoxOX0KTC5XeC5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXt2
-YXIgcyxyLHEscCxvPSJjb2xsYXBzZWQiCnQuTy5hKGEpCnM9dGhpcy5hCnI9Si5ZRShzKQpxPXRoaXMu
-YgpwPUouWUUocSkKaWYoIXIuZ1AocykudGcoMCxvKSl7ci5nUChzKS5pKDAsbykKcC5nUChxKS5pKDAs
-byl9ZWxzZXtyLmdQKHMpLlIoMCxvKQpwLmdQKHEpLlIoMCxvKX19LAokUzoxfQpMLkFPLnByb3RvdHlw
-ZT17CiQxOmZ1bmN0aW9uKGEpe3ZhciBzPUoucUYodC5nLmEoYSkpLHI9cy4kdGkscT1yLkMoIn4oMSk/
-IikuYShuZXcgTC5kTih0aGlzLmEpKQp0LlouYShudWxsKQpXLkpFKHMuYSxzLmIscSwhMSxyLmMpfSwK
-JFM6M30KTC5kTi5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXtyZXR1cm4gTC50Mih0Lk8uYShhKSx0
-aGlzLmEpfSwKJFM6Nn0KTC5Iby5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXt2YXIgcyxyLHEKdC5n
-LmEoYSkKcz1KLnFGKGEpCnI9cy4kdGkKcT1yLkMoIn4oMSk/IikuYShuZXcgTC54eihhLHRoaXMuYSkp
-CnQuWi5hKG51bGwpClcuSkUocy5hLHMuYixxLCExLHIuYyl9LAokUzozfQpMLnh6LnByb3RvdHlwZT17
-CiQxOmZ1bmN0aW9uKGEpe3ZhciBzCnQuTy5hKGEpCnM9dGhpcy5hCkwuaFgodGhpcy5iLFAuUUEocy5n
-ZXRBdHRyaWJ1dGUoImRhdGEtIituZXcgVy5TeShuZXcgVy5pNyhzKSkuT1UoIm9mZnNldCIpKSxudWxs
-KSxQLlFBKHMuZ2V0QXR0cmlidXRlKCJkYXRhLSIrbmV3IFcuU3kobmV3IFcuaTcocykpLk9VKCJsaW5l
-IikpLG51bGwpKX0sCiRTOjF9CkwuSUMucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7dmFyIHM9Si5x
-Rih0LmcuYShhKSkscj1zLiR0aQpyLkMoIn4oMSk/IikuYShMLmlTKCkpCnQuWi5hKG51bGwpClcuSkUo
-cy5hLHMuYixMLmlTKCksITEsci5jKX0sCiRTOjN9CkwuZkMucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24o
-YSl7dC5lUS5hKGEpCnRoaXMuYS5hTSgwLHRoaXMuYil9LAokUzo0N30KTC5UbS5wcm90b3R5cGU9ewok
-MTpmdW5jdGlvbihhKXtILmgoYSkKcmV0dXJuIGEubGVuZ3RoPjQwP0oubGQoYSwwLDQwKSsiLi4uIjph
-fSwKJFM6NDh9CkwublQucHJvdG90eXBlPXsKJDA6ZnVuY3Rpb24oKXtMLkZyKHRoaXMuYSx0aGlzLmIs
-dGhpcy5jKX0sCiRTOjJ9CkwuTlkucHJvdG90eXBlPXsKJDA6ZnVuY3Rpb24oKXtMLkZyKHRoaXMuYSxu
-dWxsLG51bGwpfSwKJFM6Mn0KTC51ZS5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXt0LmF3LmEoYSkK
-cmV0dXJuIEguRWooYS5xKDAsInNldmVyaXR5IikpKyIgLSAiK0guRWooYS5xKDAsIm1lc3NhZ2UiKSkr
-IiBhdCAiK0guRWooYS5xKDAsImxvY2F0aW9uIikpKyIgLSAoIitILkVqKGEucSgwLCJjb2RlIikpKyIp
-In0sCiRTOjQ5fQpMLmVYLnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3QuZy5hKGEpCiQuekIoKS50
-b1N0cmluZwp0LmRILmEoJC5vdygpLnEoMCwiaGxqcyIpKS5WNygiaGlnaGxpZ2h0QmxvY2siLFthXSl9
-LAokUzozfQpMLkVFLnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3ZhciBzLHIKdC5PLmEoYSkucHJl
-dmVudERlZmF1bHQoKQpzPXRoaXMuYQpyPXRoaXMuYgpMLmFmKHdpbmRvdy5sb2NhdGlvbi5wYXRobmFt
-ZSxzLHIsITAsbmV3IEwuUUwocyxyKSkKTC5oWCh0aGlzLmMscyxyKX0sCiRTOjF9CkwuUUwucHJvdG90
-eXBlPXsKJDA6ZnVuY3Rpb24oKXtMLkZyKHdpbmRvdy5sb2NhdGlvbi5wYXRobmFtZSx0aGlzLmEsdGhp
-cy5iKX0sCiRTOjJ9CkwuVlMucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7dmFyIHMscj0ic2VsZWN0
-ZWQtZmlsZSIKdC5nLmEoYSkKYS50b1N0cmluZwpzPUouWUUoYSkKaWYoYS5nZXRBdHRyaWJ1dGUoImRh
-dGEtIituZXcgVy5TeShuZXcgVy5pNyhhKSkuT1UoIm5hbWUiKSk9PT10aGlzLmEuYSlzLmdQKGEpLmko
-MCxyKQplbHNlIHMuZ1AoYSkuUigwLHIpfSwKJFM6M30KTC5URC5wcm90b3R5cGU9ewokMTpmdW5jdGlv
-bihhKXt2YXIgcyxyCnQuTy5hKGEpCnM9dGhpcy5hCnN3aXRjaChzLmdMKCkpe2Nhc2UgQy5jdzpicmVh
-awpjYXNlIEMuV0Q6cy5uRygpCmJyZWFrCmNhc2UgQy5YajpzLmMyKCkKYnJlYWsKY2FzZSBDLmRjOnMu
-YzIoKQpicmVha31yPXRoaXMuYgpMLmJMKHIscykKTC54bih0aGlzLmMscykKTC5BUihyLHMpfSwKJFM6
-MX0KTC5JZi5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXt2YXIgcwp0Lk8uYShhKQpzPXRoaXMuYQpM
-Lk90KHMpCkwueG4odGhpcy5iLHMpCkwuQVIodGhpcy5jLHMpfSwKJFM6MX0KTC50Qi5wcm90b3R5cGU9
-ewokMTpmdW5jdGlvbihhKXtyZXR1cm4gTC50Mih0Lk8uYShhKSwhMCl9LAokUzo2fQpMLm0yLnByb3Rv
-dHlwZT17CiQxOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLlJJKHQuTy5hKGEpKX0sClJJOmZ1bmN0aW9u
-KGEpe3ZhciBzPTAscj1QLkZYKHQuUCkscT0xLHAsbz1bXSxuPXRoaXMsbSxsLGssaixpLGgsZyxmCnZh
-ciAkYXN5bmMkJDE9UC5seihmdW5jdGlvbihiLGMpe2lmKGI9PT0xKXtwPWMKcz1xfXdoaWxlKHRydWUp
-c3dpdGNoKHMpe2Nhc2UgMDpxPTMKaT1kb2N1bWVudAptPUMuQ0QuelEoaS5xdWVyeVNlbGVjdG9yKCIu
-Y29udGVudCIpLnNjcm9sbFRvcCkKaD10LlgKcz02CnJldHVybiBQLmpRKEwudHkoTC5RNCgiL2FwcGx5
-LWhpbnQiLFAuRmwoaCxoKSksbi5hLkx0KCkpLCRhc3luYyQkMSkKY2FzZSA2Omg9bi5iCmw9TC5Vcyho
-LmEpCnM9NwpyZXR1cm4gUC5qUShMLkc3KGwsbnVsbCxoLmIsITEsbnVsbCksJGFzeW5jJCQxKQpjYXNl
-IDc6aS5ib2R5LmNsYXNzTGlzdC5hZGQoIm5lZWRzLXJlcnVuIikKaT1pLnF1ZXJ5U2VsZWN0b3IoIi5j
-b250ZW50IikKaS50b1N0cmluZwppLnNjcm9sbFRvcD1KLlZ1KG0pCnE9MQpzPTUKYnJlYWsKY2FzZSAz
-OnE9MgpmPXAKaz1ILlJ1KGYpCmo9SC50cyhmKQpMLkMyKCJjb3VsZG4ndCBhcHBseSBoaW50IixrLGop
-CnM9NQpicmVhawpjYXNlIDI6cz0xCmJyZWFrCmNhc2UgNTpyZXR1cm4gUC55QyhudWxsLHIpCmNhc2Ug
-MTpyZXR1cm4gUC5mMyhwLHIpfX0pCnJldHVybiBQLkRJKCRhc3luYyQkMSxyKX0sCiRTOjIwfQpMLlFX
-LnByb3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuYSsiOlxuIit0aGlzLmJ9LAokaVJ6
-OjF9CkwuWEEucHJvdG90eXBlPXsKRWI6ZnVuY3Rpb24oYSxiLGMpe3JldHVybiEwfSwKaTA6ZnVuY3Rp
-b24oYSl7cmV0dXJuITB9LAokaWtGOjF9CkwudnQucHJvdG90eXBlPXsKZ0w6ZnVuY3Rpb24oKXt2YXIg
-cyxyLHEscCxvLG4sbSxsPXRoaXMuZAppZihsLmxlbmd0aD09PTApcmV0dXJuIEMuY3cKcz1DLk5tLmd0
-SChsKS5nTCgpCmZvcihyPWwubGVuZ3RoLHE9ITAscD0hMCxvPTA7bzxsLmxlbmd0aDtsLmxlbmd0aD09
-PXJ8fCgwLEgubGspKGwpLCsrbyl7bj1sW29dLmdMKCkKaWYobiE9cylzPW51bGwKbT1uIT09Qy5jdwpp
-ZihtJiZuIT09Qy5XRClxPSExCmlmKG0mJm4hPT1DLlhqKXA9ITF9aWYocyE9bnVsbClyZXR1cm4gcwpp
-ZihxKXJldHVybiBDLldECmlmKHApcmV0dXJuIEMuWGoKcmV0dXJuIEMuZGN9LApMVjpmdW5jdGlvbigp
-e3ZhciBzLHIscT10aGlzLmQKaWYocSE9bnVsbClmb3Iocz1xLmxlbmd0aCxyPTA7cjxzOysrcilxW3Jd
-LmI9dGhpc30sCmMyOmZ1bmN0aW9uKCl7dmFyIHMscixxLHAKZm9yKHM9dGhpcy5kLHI9cy5sZW5ndGgs
-cT0wO3E8cy5sZW5ndGg7cy5sZW5ndGg9PT1yfHwoMCxILmxrKShzKSwrK3Epe3A9c1txXQppZihwIGlu
-c3RhbmNlb2YgTC52dClwLmMyKCkKZWxzZSBpZihwIGluc3RhbmNlb2YgTC5jRCYmSC5vVChwLngpJiZw
-LnI9PT1DLlhqKXAucj1DLldEfX0sCm5HOmZ1bmN0aW9uKCl7dmFyIHMscixxLHAKZm9yKHM9dGhpcy5k
-LHI9cy5sZW5ndGgscT0wO3E8cy5sZW5ndGg7cy5sZW5ndGg9PT1yfHwoMCxILmxrKShzKSwrK3Epe3A9
-c1txXQppZihwIGluc3RhbmNlb2YgTC52dClwLm5HKCkKZWxzZSBpZihwIGluc3RhbmNlb2YgTC5jRCYm
-SC5vVChwLngpJiZwLnI9PT1DLldEKXAucj1DLlhqfX0sCkx0OmZ1bmN0aW9uKCl7dmFyIHMscj1QLkZs
-KHQuWCx0Ll8pCnIuWTUoMCwidHlwZSIsImRpcmVjdG9yeSIpCnIuWTUoMCwibmFtZSIsdGhpcy5hKQpy
-Llk1KDAsInN1YnRyZWUiLEwuVkQodGhpcy5kKSkKcz10aGlzLmMKaWYocyE9bnVsbClyLlk1KDAsInBh
-dGgiLHMpCnJldHVybiByfX0KTC5jRC5wcm90b3R5cGU9ewpMdDpmdW5jdGlvbigpe3ZhciBzLHI9dGhp
-cyxxPVAuRmwodC5YLHQuXykKcS5ZNSgwLCJ0eXBlIiwiZmlsZSIpCnEuWTUoMCwibmFtZSIsci5hKQpz
-PXIuYwppZihzIT1udWxsKXEuWTUoMCwicGF0aCIscykKcz1yLmQKaWYocyE9bnVsbClxLlk1KDAsImhy
-ZWYiLHMpCnM9ci5lCmlmKHMhPW51bGwpcS5ZNSgwLCJlZGl0Q291bnQiLHMpCnM9ci5mCmlmKHMhPW51
-bGwpcS5ZNSgwLCJ3YXNFeHBsaWNpdGx5T3B0ZWRPdXQiLHMpCnM9ci5yCmlmKHMhPW51bGwpcS5ZNSgw
-LCJtaWdyYXRpb25TdGF0dXMiLHMuYSkKcz1yLngKaWYocyE9bnVsbClxLlk1KDAsIm1pZ3JhdGlvblN0
-YXR1c0NhbkJlQ2hhbmdlZCIscykKcmV0dXJuIHF9LApnTDpmdW5jdGlvbigpe3JldHVybiB0aGlzLnJ9
-fQpMLkQ4LnByb3RvdHlwZT17fQpMLk85LnByb3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7cmV0dXJuIHRo
-aXMuYn19CkwuR2IucHJvdG90eXBlPXsKdzpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5ifX0KUi5MTC5w
-cm90b3R5cGU9ewpMdDpmdW5jdGlvbigpe3JldHVybiBQLkVGKFsibm9kZUlkIix0aGlzLmIsImtpbmQi
-LHRoaXMuYS5hXSx0LlgsdC5fKX19ClIuTUQucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7cmV0dXJu
-IHQuZkUuYShhKS5hPT09dGhpcy5hLnEoMCwia2luZCIpfSwKJFM6NTB9ClIuSDcucHJvdG90eXBlPXsK
-dzpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5ifX0KTS5sSS5wcm90b3R5cGU9ewpXTzpmdW5jdGlvbihh
-LGIpe3ZhciBzLHIscT10LmQ0Ck0uWUYoImFic29sdXRlIixILlFJKFtiLG51bGwsbnVsbCxudWxsLG51
-bGwsbnVsbCxudWxsXSxxKSkKcz10aGlzLmEKcz1zLllyKGIpPjAmJiFzLmhLKGIpCmlmKHMpcmV0dXJu
-IGIKcz1ELmFiKCkKcj1ILlFJKFtzLGIsbnVsbCxudWxsLG51bGwsbnVsbCxudWxsLG51bGxdLHEpCk0u
-WUYoImpvaW4iLHIpCnJldHVybiB0aGlzLklQKG5ldyBILnU2KHIsdC5lSikpfSwKemY6ZnVuY3Rpb24o
-YSl7dmFyIHMscixxPVguQ0woYSx0aGlzLmEpCnEuSXgoKQpzPXEuZApyPXMubGVuZ3RoCmlmKHI9PT0w
-KXtzPXEuYgpyZXR1cm4gcz09bnVsbD8iLiI6c31pZihyPT09MSl7cz1xLmIKcmV0dXJuIHM9PW51bGw/
-Ii4iOnN9aWYoMD49cilyZXR1cm4gSC5PSChzLC0xKQpzLnBvcCgpCnM9cS5lCmlmKDA+PXMubGVuZ3Ro
-KXJldHVybiBILk9IKHMsLTEpCnMucG9wKCkKcS5JeCgpCnJldHVybiBxLncoMCl9LApJUDpmdW5jdGlv
-bihhKXt2YXIgcyxyLHEscCxvLG4sbSxsLGssagp0LlEuYShhKQpmb3Iocz1hLiR0aSxyPXMuQygiYTIo
-Y1guRSkiKS5hKG5ldyBNLnE3KCkpLHE9YS5nbShhKSxzPW5ldyBILlNPKHEscixzLkMoIlNPPGNYLkU+
-IikpLHI9dGhpcy5hLHA9ITEsbz0hMSxuPSIiO3MuRigpOyl7bT1xLmdsKCkKaWYoci5oSyhtKSYmbyl7
-bD1YLkNMKG0scikKaz1uLmNoYXJDb2RlQXQoMCk9PTA/bjpuCm49Qy54Qi5OaihrLDAsci5TcChrLCEw
-KSkKbC5iPW4KaWYoci5kcyhuKSlDLk5tLlk1KGwuZSwwLHIuZ21JKCkpCm49bC53KDApfWVsc2UgaWYo
-ci5ZcihtKT4wKXtvPSFyLmhLKG0pCm49SC5FaihtKX1lbHNle2o9bS5sZW5ndGgKaWYoaiE9PTApe2lm
-KDA+PWopcmV0dXJuIEguT0gobSwwKQpqPXIuVWQobVswXSl9ZWxzZSBqPSExCmlmKCFqKWlmKHApbis9
-ci5nbUkoKQpuKz1tfXA9ci5kcyhtKX1yZXR1cm4gbi5jaGFyQ29kZUF0KDApPT0wP246bn0sCm81OmZ1
-bmN0aW9uKGEpe3ZhciBzCmlmKCF0aGlzLnkzKGEpKXJldHVybiBhCnM9WC5DTChhLHRoaXMuYSkKcy5y
-UigpCnJldHVybiBzLncoMCl9LAp5MzpmdW5jdGlvbihhKXt2YXIgcyxyLHEscCxvLG4sbSxsLGssagph
-LnRvU3RyaW5nCnM9dGhpcy5hCnI9cy5ZcihhKQppZihyIT09MCl7aWYocz09PSQuS2soKSlmb3IocT0w
-O3E8cjsrK3EpaWYoQy54Qi5XKGEscSk9PT00NylyZXR1cm4hMApwPXIKbz00N31lbHNle3A9MApvPW51
-bGx9Zm9yKG49bmV3IEgucWooYSkuYSxtPW4ubGVuZ3RoLHE9cCxsPW51bGw7cTxtOysrcSxsPW8sbz1r
-KXtrPUMueEIuTyhuLHEpCmlmKHMucjQoaykpe2lmKHM9PT0kLktrKCkmJms9PT00NylyZXR1cm4hMApp
-ZihvIT1udWxsJiZzLnI0KG8pKXJldHVybiEwCmlmKG89PT00NilqPWw9PW51bGx8fGw9PT00Nnx8cy5y
-NChsKQplbHNlIGo9ITEKaWYoailyZXR1cm4hMH19aWYobz09bnVsbClyZXR1cm4hMAppZihzLnI0KG8p
-KXJldHVybiEwCmlmKG89PT00NilzPWw9PW51bGx8fHMucjQobCl8fGw9PT00NgplbHNlIHM9ITEKaWYo
-cylyZXR1cm4hMApyZXR1cm4hMX0sCkhQOmZ1bmN0aW9uKGEsYil7dmFyIHMscixxLHAsbyxuLG0sbD10
-aGlzLGs9J1VuYWJsZSB0byBmaW5kIGEgcGF0aCB0byAiJwpiPWwuV08oMCxiKQpzPWwuYQppZihzLlly
-KGIpPD0wJiZzLllyKGEpPjApcmV0dXJuIGwubzUoYSkKaWYocy5ZcihhKTw9MHx8cy5oSyhhKSlhPWwu
-V08oMCxhKQppZihzLllyKGEpPD0wJiZzLllyKGIpPjApdGhyb3cgSC5iKFguSTcoaytILkVqKGEpKyci
-IGZyb20gIicrSC5FaihiKSsnIi4nKSkKcj1YLkNMKGIscykKci5yUigpCnE9WC5DTChhLHMpCnEuclIo
-KQpwPXIuZApvPXAubGVuZ3RoCmlmKG8hPT0wKXtpZigwPj1vKXJldHVybiBILk9IKHAsMCkKcD1KLlJN
-KHBbMF0sIi4iKX1lbHNlIHA9ITEKaWYocClyZXR1cm4gcS53KDApCnA9ci5iCm89cS5iCmlmKHAhPW8p
-cD1wPT1udWxsfHxvPT1udWxsfHwhcy5OYyhwLG8pCmVsc2UgcD0hMQppZihwKXJldHVybiBxLncoMCkK
-d2hpbGUoITApe3A9ci5kCm89cC5sZW5ndGgKaWYobyE9PTApe249cS5kCm09bi5sZW5ndGgKaWYobSE9
-PTApe2lmKDA+PW8pcmV0dXJuIEguT0gocCwwKQpwPXBbMF0KaWYoMD49bSlyZXR1cm4gSC5PSChuLDAp
-Cm49cy5OYyhwLG5bMF0pCnA9bn1lbHNlIHA9ITF9ZWxzZSBwPSExCmlmKCFwKWJyZWFrCkMuTm0uVzQo
-ci5kLDApCkMuTm0uVzQoci5lLDEpCkMuTm0uVzQocS5kLDApCkMuTm0uVzQocS5lLDEpfXA9ci5kCm89
-cC5sZW5ndGgKaWYobyE9PTApe2lmKDA+PW8pcmV0dXJuIEguT0gocCwwKQpwPUouUk0ocFswXSwiLi4i
-KX1lbHNlIHA9ITEKaWYocCl0aHJvdyBILmIoWC5JNyhrK0guRWooYSkrJyIgZnJvbSAiJytILkVqKGIp
-KyciLicpKQpwPXQuTgpDLk5tLlVHKHEuZCwwLFAuTzgoci5kLmxlbmd0aCwiLi4iLCExLHApKQpDLk5t
-Llk1KHEuZSwwLCIiKQpDLk5tLlVHKHEuZSwxLFAuTzgoci5kLmxlbmd0aCxzLmdtSSgpLCExLHApKQpz
-PXEuZApwPXMubGVuZ3RoCmlmKHA9PT0wKXJldHVybiIuIgppZihwPjEmJkouUk0oQy5ObS5ncloocyks
-Ii4iKSl7cz1xLmQKaWYoMD49cy5sZW5ndGgpcmV0dXJuIEguT0gocywtMSkKcy5wb3AoKQpzPXEuZQpp
-ZigwPj1zLmxlbmd0aClyZXR1cm4gSC5PSChzLC0xKQpzLnBvcCgpCmlmKDA+PXMubGVuZ3RoKXJldHVy
-biBILk9IKHMsLTEpCnMucG9wKCkKQy5ObS5pKHMsIiIpfXEuYj0iIgpxLkl4KCkKcmV0dXJuIHEudygw
-KX19Ck0ucTcucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7cmV0dXJuIEguaChhKSE9PSIifSwKJFM6
-N30KTS5Oby5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXtILmsoYSkKcmV0dXJuIGE9PW51bGw/Im51
-bGwiOiciJythKyciJ30sCiRTOjUxfQpCLmZ2LnByb3RvdHlwZT17CnhaOmZ1bmN0aW9uKGEpe3ZhciBz
-LHI9dGhpcy5ZcihhKQppZihyPjApcmV0dXJuIEoubGQoYSwwLHIpCmlmKHRoaXMuaEsoYSkpe2lmKDA+
-PWEubGVuZ3RoKXJldHVybiBILk9IKGEsMCkKcz1hWzBdfWVsc2Ugcz1udWxsCnJldHVybiBzfSwKTmM6
-ZnVuY3Rpb24oYSxiKXtyZXR1cm4gYT09Yn19ClguV0QucHJvdG90eXBlPXsKSXg6ZnVuY3Rpb24oKXt2
-YXIgcyxyLHE9dGhpcwp3aGlsZSghMCl7cz1xLmQKaWYoIShzLmxlbmd0aCE9PTAmJkouUk0oQy5ObS5n
-cloocyksIiIpKSlicmVhawpzPXEuZAppZigwPj1zLmxlbmd0aClyZXR1cm4gSC5PSChzLC0xKQpzLnBv
-cCgpCnM9cS5lCmlmKDA+PXMubGVuZ3RoKXJldHVybiBILk9IKHMsLTEpCnMucG9wKCl9cz1xLmUKcj1z
-Lmxlbmd0aAppZihyIT09MClDLk5tLlk1KHMsci0xLCIiKX0sCnJSOmZ1bmN0aW9uKCl7dmFyIHMscixx
-LHAsbyxuLG09dGhpcyxsPUguUUkoW10sdC5zKQpmb3Iocz1tLmQscj1zLmxlbmd0aCxxPTAscD0wO3A8
-cy5sZW5ndGg7cy5sZW5ndGg9PT1yfHwoMCxILmxrKShzKSwrK3Ape289c1twXQpuPUouaWEobykKaWYo
-IShuLkROKG8sIi4iKXx8bi5ETihvLCIiKSkpaWYobi5ETihvLCIuLiIpKXtuPWwubGVuZ3RoCmlmKG4h
-PT0wKXtpZigwPj1uKXJldHVybiBILk9IKGwsLTEpCmwucG9wKCl9ZWxzZSArK3F9ZWxzZSBDLk5tLmko
-bCxvKX1pZihtLmI9PW51bGwpQy5ObS5VRyhsLDAsUC5POChxLCIuLiIsITEsdC5OKSkKaWYobC5sZW5n
-dGg9PT0wJiZtLmI9PW51bGwpQy5ObS5pKGwsIi4iKQptLnNuSihsKQpzPW0uYQptLnNQaChQLk84KGwu
-bGVuZ3RoKzEscy5nbUkoKSwhMCx0Lk4pKQpyPW0uYgppZihyPT1udWxsfHxsLmxlbmd0aD09PTB8fCFz
-LmRzKHIpKUMuTm0uWTUobS5lLDAsIiIpCnI9bS5iCmlmKHIhPW51bGwmJnM9PT0kLktrKCkpe3IudG9T
-dHJpbmcKbS5iPUgueXMociwiLyIsIlxcIil9bS5JeCgpfSwKdzpmdW5jdGlvbihhKXt2YXIgcyxyLHE9
-dGhpcyxwPXEuYgpwPXAhPW51bGw/cDoiIgpmb3Iocz0wO3M8cS5kLmxlbmd0aDsrK3Mpe3I9cS5lCmlm
-KHM+PXIubGVuZ3RoKXJldHVybiBILk9IKHIscykKcj1wK0guRWoocltzXSkKcD1xLmQKaWYocz49cC5s
-ZW5ndGgpcmV0dXJuIEguT0gocCxzKQpwPXIrSC5FaihwW3NdKX1wKz1ILkVqKEMuTm0uZ3JaKHEuZSkp
-CnJldHVybiBwLmNoYXJDb2RlQXQoMCk9PTA/cDpwfSwKc25KOmZ1bmN0aW9uKGEpe3RoaXMuZD10LkQu
-YShhKX0sCnNQaDpmdW5jdGlvbihhKXt0aGlzLmU9dC5ELmEoYSl9fQpYLmR2LnByb3RvdHlwZT17Cnc6
-ZnVuY3Rpb24oYSl7cmV0dXJuIlBhdGhFeGNlcHRpb246ICIrdGhpcy5hfSwKJGlSejoxfQpPLnpMLnBy
-b3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuZ29jKHRoaXMpfX0KRS5PRi5wcm90b3R5
-cGU9ewpVZDpmdW5jdGlvbihhKXtyZXR1cm4gQy54Qi50ZyhhLCIvIil9LApyNDpmdW5jdGlvbihhKXty
-ZXR1cm4gYT09PTQ3fSwKZHM6ZnVuY3Rpb24oYSl7dmFyIHM9YS5sZW5ndGgKcmV0dXJuIHMhPT0wJiZD
-LnhCLk8oYSxzLTEpIT09NDd9LApTcDpmdW5jdGlvbihhLGIpe2lmKGEubGVuZ3RoIT09MCYmQy54Qi5X
-KGEsMCk9PT00NylyZXR1cm4gMQpyZXR1cm4gMH0sCllyOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLlNw
-KGEsITEpfSwKaEs6ZnVuY3Rpb24oYSl7cmV0dXJuITF9LApnb2M6ZnVuY3Rpb24oKXtyZXR1cm4icG9z
-aXgifSwKZ21JOmZ1bmN0aW9uKCl7cmV0dXJuIi8ifX0KRi5ydS5wcm90b3R5cGU9ewpVZDpmdW5jdGlv
-bihhKXtyZXR1cm4gQy54Qi50ZyhhLCIvIil9LApyNDpmdW5jdGlvbihhKXtyZXR1cm4gYT09PTQ3fSwK
-ZHM6ZnVuY3Rpb24oYSl7dmFyIHM9YS5sZW5ndGgKaWYocz09PTApcmV0dXJuITEKaWYoQy54Qi5PKGEs
-cy0xKSE9PTQ3KXJldHVybiEwCnJldHVybiBDLnhCLlRjKGEsIjovLyIpJiZ0aGlzLllyKGEpPT09c30s
-ClNwOmZ1bmN0aW9uKGEsYil7dmFyIHMscixxLHAsbz1hLmxlbmd0aAppZihvPT09MClyZXR1cm4gMApp
-ZihDLnhCLlcoYSwwKT09PTQ3KXJldHVybiAxCmZvcihzPTA7czxvOysrcyl7cj1DLnhCLlcoYSxzKQpp
-ZihyPT09NDcpcmV0dXJuIDAKaWYocj09PTU4KXtpZihzPT09MClyZXR1cm4gMApxPUMueEIuWFUoYSwi
-LyIsQy54Qi5RaShhLCIvLyIscysxKT9zKzM6cykKaWYocTw9MClyZXR1cm4gbwppZighYnx8bzxxKzMp
-cmV0dXJuIHEKaWYoIUMueEIubkMoYSwiZmlsZTovLyIpKXJldHVybiBxCmlmKCFCLll1KGEscSsxKSly
-ZXR1cm4gcQpwPXErMwpyZXR1cm4gbz09PXA/cDpxKzR9fXJldHVybiAwfSwKWXI6ZnVuY3Rpb24oYSl7
-cmV0dXJuIHRoaXMuU3AoYSwhMSl9LApoSzpmdW5jdGlvbihhKXtyZXR1cm4gYS5sZW5ndGghPT0wJiZD
-LnhCLlcoYSwwKT09PTQ3fSwKZ29jOmZ1bmN0aW9uKCl7cmV0dXJuInVybCJ9LApnbUk6ZnVuY3Rpb24o
-KXtyZXR1cm4iLyJ9fQpMLklWLnByb3RvdHlwZT17ClVkOmZ1bmN0aW9uKGEpe3JldHVybiBDLnhCLnRn
-KGEsIi8iKX0sCnI0OmZ1bmN0aW9uKGEpe3JldHVybiBhPT09NDd8fGE9PT05Mn0sCmRzOmZ1bmN0aW9u
-KGEpe3ZhciBzPWEubGVuZ3RoCmlmKHM9PT0wKXJldHVybiExCnM9Qy54Qi5PKGEscy0xKQpyZXR1cm4h
-KHM9PT00N3x8cz09PTkyKX0sClNwOmZ1bmN0aW9uKGEsYil7dmFyIHMscixxPWEubGVuZ3RoCmlmKHE9
-PT0wKXJldHVybiAwCnM9Qy54Qi5XKGEsMCkKaWYocz09PTQ3KXJldHVybiAxCmlmKHM9PT05Mil7aWYo
-cTwyfHxDLnhCLlcoYSwxKSE9PTkyKXJldHVybiAxCnI9Qy54Qi5YVShhLCJcXCIsMikKaWYocj4wKXty
-PUMueEIuWFUoYSwiXFwiLHIrMSkKaWYocj4wKXJldHVybiByfXJldHVybiBxfWlmKHE8MylyZXR1cm4g
-MAppZighQi5PUyhzKSlyZXR1cm4gMAppZihDLnhCLlcoYSwxKSE9PTU4KXJldHVybiAwCnE9Qy54Qi5X
-KGEsMikKaWYoIShxPT09NDd8fHE9PT05MikpcmV0dXJuIDAKcmV0dXJuIDN9LApZcjpmdW5jdGlvbihh
-KXtyZXR1cm4gdGhpcy5TcChhLCExKX0sCmhLOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLllyKGEpPT09
-MX0sCk90OmZ1bmN0aW9uKGEsYil7dmFyIHMKaWYoYT09PWIpcmV0dXJuITAKaWYoYT09PTQ3KXJldHVy
-biBiPT09OTIKaWYoYT09PTkyKXJldHVybiBiPT09NDcKaWYoKGFeYikhPT0zMilyZXR1cm4hMQpzPWF8
-MzIKcmV0dXJuIHM+PTk3JiZzPD0xMjJ9LApOYzpmdW5jdGlvbihhLGIpe3ZhciBzLHIscQppZihhPT1i
-KXJldHVybiEwCnM9YS5sZW5ndGgKaWYocyE9PWIubGVuZ3RoKXJldHVybiExCmZvcihyPUouclkoYiks
-cT0wO3E8czsrK3EpaWYoIXRoaXMuT3QoQy54Qi5XKGEscSksci5XKGIscSkpKXJldHVybiExCnJldHVy
-biEwfSwKZ29jOmZ1bmN0aW9uKCl7cmV0dXJuIndpbmRvd3MifSwKZ21JOmZ1bmN0aW9uKCl7cmV0dXJu
-IlxcIn19OyhmdW5jdGlvbiBhbGlhc2VzKCl7dmFyIHM9Si5Hdi5wcm90b3R5cGUKcy5VPXMudwpzLlNq
-PXMuZTcKcz1KLk1GLnByb3RvdHlwZQpzLnQ9cy53CnM9UC5jWC5wcm90b3R5cGUKcy5HRz1zLmV2CnM9
-UC5NaC5wcm90b3R5cGUKcy54Yj1zLncKcz1XLmN2LnByb3RvdHlwZQpzLkRXPXMucjYKcz1XLm02LnBy
-b3RvdHlwZQpzLmpGPXMuRWIKcz1QLkU0LnByb3RvdHlwZQpzLlVyPXMucQpzLmU0PXMuWTUKcz1QLnZn
-LnByb3RvdHlwZQpzLmJoPXMuWTV9KSgpOyhmdW5jdGlvbiBpbnN0YWxsVGVhck9mZnMoKXt2YXIgcz1o
-dW5rSGVscGVycy5fc3RhdGljXzEscj1odW5rSGVscGVycy5fc3RhdGljXzAscT1odW5rSGVscGVycy5p
-bnN0YWxsSW5zdGFuY2VUZWFyT2ZmLHA9aHVua0hlbHBlcnMuaW5zdGFsbFN0YXRpY1RlYXJPZmYsbz1o
-dW5rSGVscGVycy5faW5zdGFuY2VfMXUKcyhQLCJFWCIsIlpWIiw4KQpzKFAsInl0Iiwib0EiLDgpCnMo
-UCwicVciLCJCeiIsOCkKcihQLCJVSSIsImVOIiwwKQpxKFAuUGYucHJvdG90eXBlLCJnWUoiLDAsMSxu
-dWxsLFsiJDIiLCIkMSJdLFsidzAiLCJwbSJdLDI2LDAsMCkKcyhQLCJDeSIsIk5DIiw0KQpzKFAsIlBI
-IiwiTXQiLDUpCnAoVywicFMiLDQsbnVsbCxbIiQ0Il0sWyJxRCJdLDksMCkKcChXLCJWNCIsNCxudWxs
-LFsiJDQiXSxbIm5aIl0sOSwwKQpvKFAuQXMucHJvdG90eXBlLCJndU0iLCJWIiw1KQpzKFAsImlHIiwi
-d1kiLDU0KQpzKFAsIncwIiwiZFUiLDM2KQpzKEwsImlTIiwiaTYiLDYpfSkoKTsoZnVuY3Rpb24gaW5o
-ZXJpdGFuY2UoKXt2YXIgcz1odW5rSGVscGVycy5taXhpbixyPWh1bmtIZWxwZXJzLmluaGVyaXQscT1o
-dW5rSGVscGVycy5pbmhlcml0TWFueQpyKFAuTWgsbnVsbCkKcShQLk1oLFtILkZLLEouR3YsSi5tMSxQ
-LmNYLEguRTcsUC5YUyxQLm5ZLEguYTcsUC5BbixILkZ1LEguSkIsSC5TVSxILlJlLEgud3YsUC5QbixI
-LldVLEguTEksSC5UcCxILmY5LEgudGUsSC5icSxILlhPLEgua3IsUC5ZayxILnZoLEguTjYsSC5WUixI
-LkVLLEguUGIsSC50USxILlNkLEguSmMsSC5HLEgubFksUC5XMyxQLmloLFAuRnksUC5HVixQLkN3LFAu
-UGYsUC5GZSxQLnZzLFAuT00sUC5xaCxQLk1PLFAua1QsUC54SSxQLm0wLFAucFIsUC5ibixQLmxtLFAu
-bEQsUC5LUCxQLmxmLFAuV1ksUC5VayxQLlNoLFAuUncsUC5ieixQLmlQLFAuazUsUC5LWSxQLkNELFAu
-YUUsUC5OMyxQLmM4LFAuWmQsUC5NLFAuRG4sUC5QRSxQLlVmLFcuaWQsVy5GayxXLkpRLFcuR20sVy52
-RCxXLm02LFcuT3csVy5XOSxXLmRXLFcubWssVy5LbyxQLmlKLFAuRTQsVS5kMixVLlNlLFUuTWwsVS55
-RCxVLndiLEIuajgsQi5xcCxULm1RLEwuWEEsTC5EOCxMLk85LEwuR2IsUi5MTCxSLkg3LE0ubEksTy56
-TCxYLldELFguZHZdKQpxKEouR3YsW0oueUUsSi53ZSxKLk1GLEouamQsSi5xSSxKLkRyLEguRVQsVy5E
-MCxXLkF6LFcuTGUsVy5OaCxXLmFlLFcuSUIsVy5OUSxXLmVhLFcuYnIsVy5TZyxXLnc3LFcuSzcsVy5Y
-VyxQLmhGXSkKcShKLk1GLFtKLmlDLEoua2QsSi5jNV0pCnIoSi5QbyxKLmpkKQpxKEoucUksW0ouYlUs
-Si5rRF0pCnEoUC5jWCxbSC5CUixILmJRLEguaTEsSC5VNSxILkFNLEgudTYsSC5YUixQLm1XLEgudW5d
-KQpxKEguQlIsW0guWnksSC5RQ10pCnIoSC5vbCxILlp5KQpyKEguVXEsSC5RQykKcihILmpWLEguVXEp
-CnEoUC5YUyxbSC5uLEgucjMsSC5HTSxQLkV6LEguYXosSC52VixILkVxLFAuQzYsSC5rUyxQLlVkLFAu
-TEssUC5jLFAubXAsUC51YixQLmRzLFAubGosUC5VVixQLnQ3LEwuUVddKQpyKFAudXksUC5uWSkKcShQ
-LnV5LFtILncyLFcud3osVy5lN10pCnIoSC5xaixILncyKQpxKEguYlEsW0guYUwsSC5NQixILmk1XSkK
-cShILmFMLFtILm5ILEgubEosUC5pOF0pCnIoSC54eSxILmkxKQpxKFAuQW4sW0guTUgsSC5TTyxILlUx
-XSkKcihILmQ1LEguQU0pCnIoUC5SVSxQLlBuKQpyKFAuR2osUC5SVSkKcihILlBELFAuR2opCnIoSC5M
-UCxILldVKQpxKEguVHAsW0guQ2osSC5sYyxILmRDLEgud04sSC5WWCxQLnRoLFAuaGEsUC5WcyxQLkZ0
-LFAueUgsUC5XTSxQLlNYLFAuR3MsUC5kYSxQLm9RLFAucFYsUC5VNyxQLnZyLFAucnQsUC5LRixQLlpM
-LFAuUlQsUC5qWixQLnJxLFAuUlcsUC5CNSxQLnVPLFAuRXYsUC5WcCxQLk9SLFAucmEsUC55USxQLnhy
-LFAuTnosUC50aSxQLldGLFAubjEsUC5jUyxQLlZDLFAuSlQsUC5SWixQLk1FLFAueTUsUC55SSxQLmM2
-LFAucWQsVy5DdixXLktTLFcuQTMsVy52TixXLlV2LFcuRWcsVy5FbyxXLldrLFcuSUEsVy5mbSxQLkUy
-LFAuamcsUC5HRSxQLk43LFAudVEsUC5QQyxQLm10LFAuUVMsUC5ucCxQLlV0LFUuYU4sVS5iMCxMLmUs
-TC5WVyxMLm9aLEwuanIsTC5xbCxMLkhpLEwuQlQsTC5QWSxMLnU4LEwuTCxMLld4LEwuQU8sTC5kTixM
-LkhvLEwueHosTC5JQyxMLmZDLEwuVG0sTC5uVCxMLk5ZLEwudWUsTC5lWCxMLkVFLEwuUUwsTC5WUyxM
-LlRELEwuSWYsTC50QixMLm0yLFIuTUQsTS5xNyxNLk5vXSkKcihILlcwLFAuRXopCnEoSC5sYyxbSC56
-eCxILnJUXSkKcihILmtZLFAuQzYpCnIoUC5pbCxQLllrKQpxKFAuaWwsW0guTjUsUC51dyxXLmNmLFcu
-U3ldKQpxKFAubVcsW0guS1csUC5xNF0pCnIoSC5MWixILkVUKQpxKEguTFosW0guUkcsSC5XQl0pCnIo
-SC5WUCxILlJHKQpyKEguRGcsSC5WUCkKcihILlpHLEguV0IpCnIoSC5QZyxILlpHKQpxKEguUGcsW0gu
-eGosSC5kRSxILlpBLEguZFQsSC5QcSxILmVFLEguVjZdKQpyKEguaU0sSC5rUykKcihQLlpmLFAuUGYp
-CnIoUC5KaSxQLm0wKQpyKFAuWHYsUC5wUikKcihQLmI2LFAuWHYpCnIoUC5WaixQLldZKQpxKFAuVWss
-W1AuQ1YsUC5aaSxQLmJ5XSkKcihQLndJLFAua1QpCnEoUC53SSxbUC5VOCxQLm9qLFAuTXgsUC5FMyxQ
-LkdZXSkKcihQLks4LFAuVWQpCnIoUC50dSxQLlNoKQpyKFAudTUsUC5aaSkKcShQLmMsW1AuYkosUC5l
-WV0pCnIoUC5xZSxQLkRuKQpxKFcuRDAsW1cudUgsVy53YSxXLks1LFcuQ21dKQpxKFcudUgsW1cuY3Ys
-Vy5ueCxXLlFGLFcuQ1FdKQpxKFcuY3YsW1cucUUsUC5oaV0pCnEoVy5xRSxbVy5HaCxXLmZZLFcuclos
-Vy5RUCxXLmg0LFcuU04sVy5scCxXLlRiLFcuSXYsVy5XUCxXLnlZXSkKcihXLm9KLFcuTGUpCnIoVy5o
-SCxXLkF6KQpyKFcuVmIsVy5RRikKcihXLmZKLFcud2EpCnEoVy5lYSxbVy53NixXLmV3XSkKcihXLkFq
-LFcudzYpCnIoVy5yQixXLks3KQpyKFcuQkgsVy5yQikKcihXLnc0LFcuSUIpCnIoVy5vYSxXLlhXKQpy
-KFcucmgsVy5vYSkKcihXLmk3LFcuY2YpCnIoUC5BcyxQLlZqKQpxKFAuQXMsW1cuSTQsUC5LZV0pCnIo
-Vy5STyxQLnFoKQpyKFcuZXUsVy5STykKcihXLnhDLFAuTU8pCnIoVy5jdCxXLm02KQpyKFAuQmYsUC5p
-SikKcShQLkU0LFtQLnI3LFAudmddKQpyKFAuVHosUC52ZykKcihQLm5kLFAuaGkpCnEoTC5EOCxbTC52
-dCxMLmNEXSkKcihCLmZ2LE8uekwpCnEoQi5mdixbRS5PRixGLnJ1LEwuSVZdKQpzKEgudzIsSC5SZSkK
-cyhILlFDLFAubEQpCnMoSC5SRyxQLmxEKQpzKEguVlAsSC5TVSkKcyhILldCLFAubEQpCnMoSC5aRyxI
-LlNVKQpzKFAublksUC5sRCkKcyhQLldZLFAubGYpCnMoUC5SVSxQLktQKQpzKFAucFIsUC5sZikKcyhX
-LkxlLFcuaWQpCnMoVy5LNyxQLmxEKQpzKFcuckIsVy5HbSkKcyhXLlhXLFAubEQpCnMoVy5vYSxXLkdt
-KQpzKFAudmcsUC5sRCl9KSgpCnZhciB2PXt0eXBlVW5pdmVyc2U6e2VDOm5ldyBNYXAoKSx0Ujp7fSxl
-VDp7fSx0UFY6e30sc0VBOltdfSxtYW5nbGVkR2xvYmFsTmFtZXM6e0lqOiJpbnQiLENQOiJkb3VibGUi
-LFpaOiJudW0iLHFVOiJTdHJpbmciLGEyOiJib29sIixjODoiTnVsbCIsek06Ikxpc3QifSxtYW5nbGVk
-TmFtZXM6e30sdHlwZXM6WyJ+KCkiLCJjOChBaiopIiwiYzgoKSIsImM4KGN2KikiLCJAKEApIiwicVUo
-cVUpIiwifihBaiopIiwiYTIocVUpIiwifih+KCkpIiwiYTIoY3YscVUscVUsSlEpIiwiYzgoQCkiLCJ+
-KE1oPyxNaD8pIiwiQCgpIiwifihxVSxAKSIsIn4objYscVUsSWopIiwifihxVSxxVSkiLCJhMihrRiki
-LCJjOChALEApIiwifih4dTxxVT4pIiwiYzgoZWEqKSIsImI4PGM4PiooQWoqKSIsIn4ocVUsSWopIiwi
-fihxVSxxVT8pIiwibjYoQCxAKSIsIn4oSWosQCkiLCJhMih1SCkiLCJ+KE1oW0d6P10pIiwifihlYSki
-LCJjOChNaCxHeikiLCJ2czxAPihAKSIsIn4odUgsdUg/KSIsIn4oQCxAKSIsIkAocVUpIiwiYTIoeHU8
-cVU+KSIsIkAoQCxxVSkiLCJyNyhAKSIsIk1oPyhAKSIsIkU0KEApIiwiTEwqKEApIiwiWjA8cVUqLE1o
-Kj4qKExMKikiLCJ+KEdELEApIiwiWjA8cVUscVU+KFowPHFVLHFVPixxVSkiLCJjOChaMDxxVSosTWgq
-PiopIiwifihAKSIsInFVKihBaiopIiwifihxVVtAXSkiLCJUejxAPihAKSIsImM4KGV3KikiLCJxVSoo
-cVUqKSIsInFVKihaMDxALEA+KikiLCJhMiooSDcqKSIsInFVKHFVPykiLCJJaihJaixJaikiLCJjOCh+
-KCkpIiwiTWg/KE1oPykiLCJjOChALEd6KSJdLGludGVyY2VwdG9yc0J5VGFnOm51bGwsbGVhZlRhZ3M6
-bnVsbCxhcnJheVJ0aTp0eXBlb2YgU3ltYm9sPT0iZnVuY3Rpb24iJiZ0eXBlb2YgU3ltYm9sKCk9PSJz
-eW1ib2wiP1N5bWJvbCgiJHRpIik6IiR0aSJ9CkgueGIodi50eXBlVW5pdmVyc2UsSlNPTi5wYXJzZSgn
-eyJpQyI6Ik1GIiwia2QiOiJNRiIsImM1IjoiTUYiLCJyeCI6ImVhIiwiZTUiOiJlYSIsIlkwIjoiaGki
-LCJ0cCI6ImhpIiwiRzgiOiJldyIsIk1yIjoicUUiLCJlTCI6InFFIiwiSTAiOiJ1SCIsImhzIjoidUgi
-LCJYZyI6IlFGIiwibnIiOiJBaiIsInk0IjoidzYiLCJhUCI6IkNtIiwieGMiOiJueCIsImtKIjoibngi
-LCJ6VSI6IkRnIiwiZGYiOiJFVCIsInlFIjp7ImEyIjpbXX0sIndlIjp7ImM4IjpbXX0sIk1GIjp7InZt
-IjpbXX0sImpkIjp7InpNIjpbIjEiXSwiYlEiOlsiMSJdLCJjWCI6WyIxIl19LCJQbyI6eyJqZCI6WyIx
-Il0sInpNIjpbIjEiXSwiYlEiOlsiMSJdLCJjWCI6WyIxIl19LCJtMSI6eyJBbiI6WyIxIl19LCJxSSI6
-eyJDUCI6W10sIlpaIjpbXX0sImJVIjp7IkNQIjpbXSwiSWoiOltdLCJaWiI6W119LCJrRCI6eyJDUCI6
-W10sIlpaIjpbXX0sIkRyIjp7InFVIjpbXSwidlgiOltdfSwiYlEiOnsiY1giOlsiMSJdfSwiQlIiOnsi
-Y1giOlsiMiJdfSwiRTciOnsiQW4iOlsiMiJdfSwiWnkiOnsiQlIiOlsiMSIsIjIiXSwiY1giOlsiMiJd
-LCJjWC5FIjoiMiJ9LCJvbCI6eyJaeSI6WyIxIiwiMiJdLCJCUiI6WyIxIiwiMiJdLCJiUSI6WyIyIl0s
-ImNYIjpbIjIiXSwiY1guRSI6IjIifSwiVXEiOnsibEQiOlsiMiJdLCJ6TSI6WyIyIl0sIkJSIjpbIjEi
-LCIyIl0sImJRIjpbIjIiXSwiY1giOlsiMiJdfSwialYiOnsiVXEiOlsiMSIsIjIiXSwibEQiOlsiMiJd
-LCJ6TSI6WyIyIl0sIkJSIjpbIjEiLCIyIl0sImJRIjpbIjIiXSwiY1giOlsiMiJdLCJsRC5FIjoiMiIs
-ImNYLkUiOiIyIn0sIm4iOnsiWFMiOltdfSwicjMiOnsiWFMiOltdfSwicWoiOnsibEQiOlsiSWoiXSwi
-UmUiOlsiSWoiXSwiek0iOlsiSWoiXSwiYlEiOlsiSWoiXSwiY1giOlsiSWoiXSwibEQuRSI6IklqIiwi
-UmUuRSI6IklqIn0sIkdNIjp7IlhTIjpbXX0sImFMIjp7ImJRIjpbIjEiXSwiY1giOlsiMSJdfSwibkgi
-OnsiYUwiOlsiMSJdLCJiUSI6WyIxIl0sImNYIjpbIjEiXSwiYUwuRSI6IjEiLCJjWC5FIjoiMSJ9LCJh
-NyI6eyJBbiI6WyIxIl19LCJpMSI6eyJjWCI6WyIyIl0sImNYLkUiOiIyIn0sInh5Ijp7ImkxIjpbIjEi
-LCIyIl0sImJRIjpbIjIiXSwiY1giOlsiMiJdLCJjWC5FIjoiMiJ9LCJNSCI6eyJBbiI6WyIyIl19LCJs
-SiI6eyJhTCI6WyIyIl0sImJRIjpbIjIiXSwiY1giOlsiMiJdLCJhTC5FIjoiMiIsImNYLkUiOiIyIn0s
-IlU1Ijp7ImNYIjpbIjEiXSwiY1guRSI6IjEifSwiU08iOnsiQW4iOlsiMSJdfSwiQU0iOnsiY1giOlsi
-MSJdLCJjWC5FIjoiMSJ9LCJkNSI6eyJBTSI6WyIxIl0sImJRIjpbIjEiXSwiY1giOlsiMSJdLCJjWC5F
-IjoiMSJ9LCJVMSI6eyJBbiI6WyIxIl19LCJNQiI6eyJiUSI6WyIxIl0sImNYIjpbIjEiXSwiY1guRSI6
-IjEifSwiRnUiOnsiQW4iOlsiMSJdfSwidTYiOnsiY1giOlsiMSJdLCJjWC5FIjoiMSJ9LCJKQiI6eyJB
-biI6WyIxIl19LCJ3MiI6eyJsRCI6WyIxIl0sIlJlIjpbIjEiXSwiek0iOlsiMSJdLCJiUSI6WyIxIl0s
-ImNYIjpbIjEiXX0sInd2Ijp7IkdEIjpbXX0sIlBEIjp7IkdqIjpbIjEiLCIyIl0sIlJVIjpbIjEiLCIy
-Il0sIlBuIjpbIjEiLCIyIl0sIktQIjpbIjEiLCIyIl0sIlowIjpbIjEiLCIyIl19LCJXVSI6eyJaMCI6
-WyIxIiwiMiJdfSwiTFAiOnsiV1UiOlsiMSIsIjIiXSwiWjAiOlsiMSIsIjIiXX0sIlhSIjp7ImNYIjpb
-IjEiXSwiY1guRSI6IjEifSwiTEkiOnsidlEiOltdfSwiVzAiOnsiWFMiOltdfSwiYXoiOnsiWFMiOltd
-fSwidlYiOnsiWFMiOltdfSwidGUiOnsiUnoiOltdfSwiWE8iOnsiR3oiOltdfSwiVHAiOnsiRUgiOltd
-fSwibGMiOnsiRUgiOltdfSwiengiOnsiRUgiOltdfSwiclQiOnsiRUgiOltdfSwiRXEiOnsiWFMiOltd
-fSwia1kiOnsiWFMiOltdfSwiTjUiOnsiWWsiOlsiMSIsIjIiXSwiRm8iOlsiMSIsIjIiXSwiWjAiOlsi
-MSIsIjIiXSwiWWsuSyI6IjEiLCJZay5WIjoiMiJ9LCJpNSI6eyJiUSI6WyIxIl0sImNYIjpbIjEiXSwi
-Y1guRSI6IjEifSwiTjYiOnsiQW4iOlsiMSJdfSwiVlIiOnsid0wiOltdLCJ2WCI6W119LCJFSyI6eyJp
-YiI6W10sIk9kIjpbXX0sIktXIjp7ImNYIjpbImliIl0sImNYLkUiOiJpYiJ9LCJQYiI6eyJBbiI6WyJp
-YiJdfSwidFEiOnsiT2QiOltdfSwidW4iOnsiY1giOlsiT2QiXSwiY1guRSI6Ik9kIn0sIlNkIjp7IkFu
-IjpbIk9kIl19LCJFVCI6eyJBUyI6W119LCJMWiI6eyJYaiI6WyIxIl0sIkVUIjpbXSwiQVMiOltdfSwi
-RGciOnsibEQiOlsiQ1AiXSwiWGoiOlsiQ1AiXSwiek0iOlsiQ1AiXSwiRVQiOltdLCJiUSI6WyJDUCJd
-LCJBUyI6W10sImNYIjpbIkNQIl0sIlNVIjpbIkNQIl0sImxELkUiOiJDUCJ9LCJQZyI6eyJsRCI6WyJJ
-aiJdLCJYaiI6WyJJaiJdLCJ6TSI6WyJJaiJdLCJFVCI6W10sImJRIjpbIklqIl0sIkFTIjpbXSwiY1gi
-OlsiSWoiXSwiU1UiOlsiSWoiXX0sInhqIjp7ImxEIjpbIklqIl0sIlhqIjpbIklqIl0sInpNIjpbIklq
-Il0sIkVUIjpbXSwiYlEiOlsiSWoiXSwiQVMiOltdLCJjWCI6WyJJaiJdLCJTVSI6WyJJaiJdLCJsRC5F
-IjoiSWoifSwiZEUiOnsibEQiOlsiSWoiXSwiWGoiOlsiSWoiXSwiek0iOlsiSWoiXSwiRVQiOltdLCJi
-USI6WyJJaiJdLCJBUyI6W10sImNYIjpbIklqIl0sIlNVIjpbIklqIl0sImxELkUiOiJJaiJ9LCJaQSI6
-eyJsRCI6WyJJaiJdLCJYaiI6WyJJaiJdLCJ6TSI6WyJJaiJdLCJFVCI6W10sImJRIjpbIklqIl0sIkFT
-IjpbXSwiY1giOlsiSWoiXSwiU1UiOlsiSWoiXSwibEQuRSI6IklqIn0sImRUIjp7ImxEIjpbIklqIl0s
-IlhqIjpbIklqIl0sInpNIjpbIklqIl0sIkVUIjpbXSwiYlEiOlsiSWoiXSwiQVMiOltdLCJjWCI6WyJJ
-aiJdLCJTVSI6WyJJaiJdLCJsRC5FIjoiSWoifSwiUHEiOnsibEQiOlsiSWoiXSwiWGoiOlsiSWoiXSwi
-ek0iOlsiSWoiXSwiRVQiOltdLCJiUSI6WyJJaiJdLCJBUyI6W10sImNYIjpbIklqIl0sIlNVIjpbIklq
-Il0sImxELkUiOiJJaiJ9LCJlRSI6eyJsRCI6WyJJaiJdLCJYaiI6WyJJaiJdLCJ6TSI6WyJJaiJdLCJF
-VCI6W10sImJRIjpbIklqIl0sIkFTIjpbXSwiY1giOlsiSWoiXSwiU1UiOlsiSWoiXSwibEQuRSI6Iklq
-In0sIlY2Ijp7ImxEIjpbIklqIl0sIm42IjpbXSwiWGoiOlsiSWoiXSwiek0iOlsiSWoiXSwiRVQiOltd
-LCJiUSI6WyJJaiJdLCJBUyI6W10sImNYIjpbIklqIl0sIlNVIjpbIklqIl0sImxELkUiOiJJaiJ9LCJr
-UyI6eyJYUyI6W119LCJpTSI6eyJYUyI6W119LCJ2cyI6eyJiOCI6WyIxIl19LCJHViI6eyJBbiI6WyIx
-Il19LCJxNCI6eyJjWCI6WyIxIl0sImNYLkUiOiIxIn0sIkN3Ijp7IlhTIjpbXX0sIlpmIjp7IlBmIjpb
-IjEiXX0sIm0wIjp7IlFtIjpbXX0sIkppIjp7Im0wIjpbXSwiUW0iOltdfSwiYjYiOnsibGYiOlsiMSJd
-LCJ4dSI6WyIxIl0sImJRIjpbIjEiXSwiY1giOlsiMSJdLCJsZi5FIjoiMSJ9LCJsbSI6eyJBbiI6WyIx
-Il19LCJtVyI6eyJjWCI6WyIxIl19LCJ1eSI6eyJsRCI6WyIxIl0sInpNIjpbIjEiXSwiYlEiOlsiMSJd
-LCJjWCI6WyIxIl19LCJpbCI6eyJZayI6WyIxIiwiMiJdLCJaMCI6WyIxIiwiMiJdfSwiWWsiOnsiWjAi
-OlsiMSIsIjIiXX0sIlBuIjp7IlowIjpbIjEiLCIyIl19LCJHaiI6eyJSVSI6WyIxIiwiMiJdLCJQbiI6
-WyIxIiwiMiJdLCJLUCI6WyIxIiwiMiJdLCJaMCI6WyIxIiwiMiJdfSwiVmoiOnsibGYiOlsiMSJdLCJ4
-dSI6WyIxIl0sImJRIjpbIjEiXSwiY1giOlsiMSJdfSwiWHYiOnsibGYiOlsiMSJdLCJ4dSI6WyIxIl0s
-ImJRIjpbIjEiXSwiY1giOlsiMSJdfSwidXciOnsiWWsiOlsicVUiLCJAIl0sIlowIjpbInFVIiwiQCJd
-LCJZay5LIjoicVUiLCJZay5WIjoiQCJ9LCJpOCI6eyJhTCI6WyJxVSJdLCJiUSI6WyJxVSJdLCJjWCI6
-WyJxVSJdLCJhTC5FIjoicVUiLCJjWC5FIjoicVUifSwiQ1YiOnsiVWsiOlsiek08SWo+IiwicVUiXSwi
-VWsuUyI6InpNPElqPiJ9LCJVOCI6eyJ3SSI6WyJ6TTxJaj4iLCJxVSJdfSwiWmkiOnsiVWsiOlsicVUi
-LCJ6TTxJaj4iXX0sIlVkIjp7IlhTIjpbXX0sIks4Ijp7IlhTIjpbXX0sImJ5Ijp7IlVrIjpbIk1oPyIs
-InFVIl0sIlVrLlMiOiJNaD8ifSwib2oiOnsid0kiOlsiTWg/IiwicVUiXX0sIk14Ijp7IndJIjpbInFV
-IiwiTWg/Il19LCJ1NSI6eyJVayI6WyJxVSIsInpNPElqPiJdLCJVay5TIjoicVUifSwiRTMiOnsid0ki
-OlsicVUiLCJ6TTxJaj4iXX0sIkdZIjp7IndJIjpbInpNPElqPiIsInFVIl19LCJDUCI6eyJaWiI6W119
-LCJJaiI6eyJaWiI6W119LCJ6TSI6eyJiUSI6WyIxIl0sImNYIjpbIjEiXX0sImliIjp7Ik9kIjpbXX0s
-Inh1Ijp7ImJRIjpbIjEiXSwiY1giOlsiMSJdfSwicVUiOnsidlgiOltdfSwiQzYiOnsiWFMiOltdfSwi
-RXoiOnsiWFMiOltdfSwiTEsiOnsiWFMiOltdfSwiYyI6eyJYUyI6W119LCJiSiI6eyJYUyI6W119LCJl
-WSI6eyJYUyI6W119LCJtcCI6eyJYUyI6W119LCJ1YiI6eyJYUyI6W119LCJkcyI6eyJYUyI6W119LCJs
-aiI6eyJYUyI6W119LCJVViI6eyJYUyI6W119LCJrNSI6eyJYUyI6W119LCJLWSI6eyJYUyI6W119LCJ0
-NyI6eyJYUyI6W119LCJDRCI6eyJSeiI6W119LCJhRSI6eyJSeiI6W119LCJaZCI6eyJHeiI6W119LCJN
-Ijp7IkJMIjpbXX0sIkRuIjp7ImlEIjpbXX0sIlVmIjp7ImlEIjpbXX0sInFlIjp7ImlEIjpbXX0sImN2
-Ijp7InVIIjpbXSwiRDAiOltdfSwiZkoiOnsiRDAiOltdfSwid2EiOnsiRDAiOltdfSwiQWoiOnsiZWEi
-OltdfSwidUgiOnsiRDAiOltdfSwiZXciOnsiZWEiOltdfSwidzYiOnsiZWEiOltdfSwiSlEiOnsia0Yi
-OltdfSwicUUiOnsiY3YiOltdLCJ1SCI6W10sIkQwIjpbXX0sIkdoIjp7ImN2IjpbXSwidUgiOltdLCJE
-MCI6W119LCJmWSI6eyJjdiI6W10sInVIIjpbXSwiRDAiOltdfSwicloiOnsiY3YiOltdLCJ1SCI6W10s
-IkQwIjpbXX0sIlFQIjp7ImN2IjpbXSwidUgiOltdLCJEMCI6W119LCJueCI6eyJ1SCI6W10sIkQwIjpb
-XX0sIlFGIjp7InVIIjpbXSwiRDAiOltdfSwiSUIiOnsidG4iOlsiWloiXX0sInd6Ijp7ImxEIjpbIjEi
-XSwiek0iOlsiMSJdLCJiUSI6WyIxIl0sImNYIjpbIjEiXSwibEQuRSI6IjEifSwiaEgiOnsiQXoiOltd
-fSwiaDQiOnsiY3YiOltdLCJ1SCI6W10sIkQwIjpbXX0sIlZiIjp7InVIIjpbXSwiRDAiOltdfSwiZTci
-OnsibEQiOlsidUgiXSwiek0iOlsidUgiXSwiYlEiOlsidUgiXSwiY1giOlsidUgiXSwibEQuRSI6InVI
-In0sIkJIIjp7ImxEIjpbInVIIl0sIkdtIjpbInVIIl0sInpNIjpbInVIIl0sIlhqIjpbInVIIl0sImJR
-IjpbInVIIl0sImNYIjpbInVIIl0sImxELkUiOiJ1SCIsIkdtLkUiOiJ1SCJ9LCJTTiI6eyJjdiI6W10s
-InVIIjpbXSwiRDAiOltdfSwibHAiOnsiY3YiOltdLCJ1SCI6W10sIkQwIjpbXX0sIlRiIjp7ImN2Ijpb
-XSwidUgiOltdLCJEMCI6W119LCJJdiI6eyJjdiI6W10sInVIIjpbXSwiRDAiOltdfSwiV1AiOnsiY3Yi
-OltdLCJ1SCI6W10sIkQwIjpbXX0sInlZIjp7ImN2IjpbXSwidUgiOltdLCJEMCI6W119LCJLNSI6eyJ2
-NiI6W10sIkQwIjpbXX0sIkNtIjp7IkQwIjpbXX0sIkNRIjp7InVIIjpbXSwiRDAiOltdfSwidzQiOnsi
-dG4iOlsiWloiXX0sInJoIjp7ImxEIjpbInVIIl0sIkdtIjpbInVIIl0sInpNIjpbInVIIl0sIlhqIjpb
-InVIIl0sImJRIjpbInVIIl0sImNYIjpbInVIIl0sImxELkUiOiJ1SCIsIkdtLkUiOiJ1SCJ9LCJjZiI6
-eyJZayI6WyJxVSIsInFVIl0sIlowIjpbInFVIiwicVUiXX0sImk3Ijp7IllrIjpbInFVIiwicVUiXSwi
-WjAiOlsicVUiLCJxVSJdLCJZay5LIjoicVUiLCJZay5WIjoicVUifSwiU3kiOnsiWWsiOlsicVUiLCJx
-VSJdLCJaMCI6WyJxVSIsInFVIl0sIllrLksiOiJxVSIsIllrLlYiOiJxVSJ9LCJJNCI6eyJsZiI6WyJx
-VSJdLCJ4dSI6WyJxVSJdLCJiUSI6WyJxVSJdLCJjWCI6WyJxVSJdLCJsZi5FIjoicVUifSwiUk8iOnsi
-cWgiOlsiMSJdfSwiZXUiOnsiUk8iOlsiMSJdLCJxaCI6WyIxIl19LCJ4QyI6eyJNTyI6WyIxIl19LCJ2
-RCI6eyJrRiI6W119LCJtNiI6eyJrRiI6W119LCJjdCI6eyJrRiI6W119LCJPdyI6eyJrRiI6W119LCJX
-OSI6eyJBbiI6WyIxIl19LCJkVyI6eyJ2NiI6W10sIkQwIjpbXX0sIm1rIjp7InkwIjpbXX0sIktvIjp7
-Im9uIjpbXX0sIkFzIjp7ImxmIjpbInFVIl0sInh1IjpbInFVIl0sImJRIjpbInFVIl0sImNYIjpbInFV
-Il19LCJyNyI6eyJFNCI6W119LCJUeiI6eyJsRCI6WyIxIl0sInpNIjpbIjEiXSwiYlEiOlsiMSJdLCJF
-NCI6W10sImNYIjpbIjEiXSwibEQuRSI6IjEifSwibmQiOnsiaGkiOltdLCJjdiI6W10sInVIIjpbXSwi
-RDAiOltdfSwiS2UiOnsibGYiOlsicVUiXSwieHUiOlsicVUiXSwiYlEiOlsicVUiXSwiY1giOlsicVUi
-XSwibGYuRSI6InFVIn0sImhpIjp7ImN2IjpbXSwidUgiOltdLCJEMCI6W119LCJRVyI6eyJYUyI6W10s
-IlJ6IjpbXX0sIlhBIjp7ImtGIjpbXX0sInZ0Ijp7IkQ4IjpbXX0sImNEIjp7IkQ4IjpbXX0sImR2Ijp7
-IlJ6IjpbXX0sIk9GIjp7ImZ2IjpbXX0sInJ1Ijp7ImZ2IjpbXX0sIklWIjp7ImZ2IjpbXX0sIm42Ijp7
-InpNIjpbIklqIl0sImJRIjpbIklqIl0sImNYIjpbIklqIl0sIkFTIjpbXX19JykpCkguRkYodi50eXBl
-VW5pdmVyc2UsSlNPTi5wYXJzZSgneyJ3MiI6MSwiUUMiOjIsIkxaIjoxLCJrVCI6MiwibVciOjEsInV5
-IjoxLCJpbCI6MiwiVmoiOjEsIlh2IjoxLCJuWSI6MSwiV1kiOjEsInBSIjoxLCJ2ZyI6MX0nKSkKdmFy
-IHU9e2w6IkNhbm5vdCBleHRyYWN0IGEgZmlsZSBwYXRoIGZyb20gYSBVUkkgd2l0aCBhIGZyYWdtZW50
-IGNvbXBvbmVudCIsaToiQ2Fubm90IGV4dHJhY3QgYSBmaWxlIHBhdGggZnJvbSBhIFVSSSB3aXRoIGEg
-cXVlcnkgY29tcG9uZW50IixqOiJDYW5ub3QgZXh0cmFjdCBhIG5vbi1XaW5kb3dzIGZpbGUgcGF0aCBm
-cm9tIGEgZmlsZSBVUkkgd2l0aCBhbiBhdXRob3JpdHkiLGc6ImBudWxsYCBlbmNvdW50ZXJlZCBhcyB0
-aGUgcmVzdWx0IGZyb20gZXhwcmVzc2lvbiB3aXRoIHR5cGUgYE5ldmVyYC4iLGQ6ImFyZWEtYW5hbHl6
-ZXIsYW5hbHl6ZXItbm5iZC1taWdyYXRpb24sdHlwZS1idWcifQp2YXIgdD0oZnVuY3Rpb24gcnRpaSgp
-e3ZhciBzPUguTjAKcmV0dXJue246cygiQ3ciKSxjUjpzKCJyWiIpLHc6cygiQXoiKSxwOnMoIlFQIiks
-Z0Y6cygiUEQ8R0QsQD4iKSxkOnMoImJRPEA+IiksaDpzKCJjdiIpLHI6cygiWFMiKSxCOnMoImVhIiks
-YVM6cygiRDAiKSxnODpzKCJSeiIpLGM4OnMoImhIIiksWTpzKCJFSCIpLGU6cygiYjg8QD4iKSxJOnMo
-IlNnIiksbzpzKCJ2USIpLGVoOnMoImNYPHVIPiIpLFE6cygiY1g8cVU+IiksdTpzKCJjWDxAPiIpLHY6
-cygiamQ8a0Y+IiksczpzKCJqZDxxVT4iKSxnTjpzKCJqZDxuNj4iKSxiOnMoImpkPEA+IiksYTpzKCJq
-ZDxJaj4iKSxkNzpzKCJqZDxTZSo+IiksaDQ6cygiamQ8ajgqPiIpLEc6cygiamQ8WjA8cVUqLE1oKj4q
-PiIpLGNROnMoImpkPEQ4Kj4iKSxpOnMoImpkPHFVKj4iKSxhQTpzKCJqZDx5RCo+IiksYUo6cygiamQ8
-d2IqPiIpLFY6cygiamQ8SWoqPiIpLGQ0OnMoImpkPHFVPz4iKSxUOnMoIndlIiksZUg6cygidm0iKSx4
-OnMoImM1IiksYVU6cygiWGo8QD4iKSxhbTpzKCJUejxAPiIpLGVvOnMoIk41PEdELEA+IiksZHo6cygi
-aEYiKSxEOnMoInpNPHFVPiIpLGo6cygiek08QD4iKSxMOnMoInpNPElqPiIpLEo6cygiWjA8cVUscVU+
-IiksZjpzKCJaMDxALEA+IiksZG86cygibEo8cVUsQD4iKSxmajpzKCJsSjxxVSoscVU+IiksZlA6cygi
-bEo8cVUqLHFVKj4iKSxkRTpzKCJFVCIpLGJtOnMoIlY2IiksQTpzKCJ1SCIpLEU6cygia0YiKSxQOnMo
-ImM4IiksSzpzKCJNaCIpLHE6cygidG48Wlo+IiksZnY6cygid0wiKSxldzpzKCJuZCIpLEM6cygieHU8
-cVU+IiksbDpzKCJHeiIpLE46cygicVUiKSxkMDpzKCJxVShxVSopIiksZzc6cygiaGkiKSxmbzpzKCJH
-RCIpLGFXOnMoInlZIiksYWs6cygiQVMiKSxnYzpzKCJuNiIpLGJKOnMoImtkIiksZHc6cygiR2o8cVUs
-cVU+IiksZEQ6cygiaUQiKSxlSjpzKCJ1NjxxVT4iKSxnNDpzKCJLNSIpLGNpOnMoInY2IiksZzI6cygi
-Q20iKSxiQzpzKCJaZjxmSio+IiksaDk6cygiQ1EiKSxhYzpzKCJlNyIpLGs6cygiZXU8QWoqPiIpLFI6
-cygid3o8Y3YqPiIpLGM6cygidnM8QD4iKSxmSjpzKCJ2czxJaj4iKSxnVjpzKCJ2czxmSio+IiksY3I6
-cygiSlEiKSx5OnMoImEyIiksYWw6cygiYTIoTWgpIiksZ1I6cygiQ1AiKSx6OnMoIkAiKSxmTzpzKCJA
-KCkiKSxiSTpzKCJAKE1oKSIpLGFnOnMoIkAoTWgsR3opIiksYlU6cygiQCh4dTxxVT4pIiksZE86cygi
-QChxVSkiKSxiODpzKCJAKEAsQCkiKSxTOnMoIklqIiksZGQ6cygiR2gqIiksZzpzKCJjdioiKSxhTDpz
-KCJlYSoiKSxhWDpzKCJMTCoiKSxmRTpzKCJINyoiKSxVOnMoImNYPEA+KiIpLGRIOnMoIkU0KiIpLGZL
-OnMoInpNPEA+KiIpLGRfOnMoInpNPGo4Kj4qIiksZHA6cygiek08WjA8cVUqLE1oKj4qPioiKSxlRTpz
-KCJ6TTxNaCo+KiIpLGF3OnMoIlowPEAsQD4qIiksdDpzKCJaMDxxVSosTWgqPioiKSxPOnMoIkFqKiIp
-LGNGOnMoIjAmKiIpLF86cygiTWgqIiksZVE6cygiZXcqIiksWDpzKCJxVSoiKSxlcTpzKCJxVSoocVUq
-KSIpLGNoOnMoIkQwPyIpLGJHOnMoImI4PGM4Pj8iKSxiazpzKCJ6TTxxVT4/IiksYk06cygiek08QD4/
-IiksY1o6cygiWjA8cVUscVU+PyIpLGM5OnMoIlowPHFVLEA+PyIpLFc6cygiTWg/IiksRjpzKCJGZTxA
-LEA+PyIpLG06cygiYm4/IiksYjc6cygiYTIoTWgpPyIpLGJ3OnMoIkAoZWEpPyIpLGZWOnMoIk1oPyhN
-aD8sTWg/KT8iKSxkQTpzKCJNaD8oQCk/IiksWjpzKCJ+KCk/IiksZWI6cygifihldyopPyIpLGRpOnMo
-IlpaIiksSDpzKCJ+IiksTTpzKCJ+KCkiKSxlQTpzKCJ+KHFVLHFVKSIpLGNBOnMoIn4ocVUsQCkiKX19
-KSgpOyhmdW5jdGlvbiBjb25zdGFudHMoKXt2YXIgcz1odW5rSGVscGVycy5tYWtlQ29uc3RMaXN0CkMu
-eG49Vy5HaC5wcm90b3R5cGUKQy5SWT1XLlFQLnByb3RvdHlwZQpDLm1IPVcuYWUucHJvdG90eXBlCkMu
-Qlo9Vy5WYi5wcm90b3R5cGUKQy5EdD1XLmZKLnByb3RvdHlwZQpDLk9rPUouR3YucHJvdG90eXBlCkMu
-Tm09Si5qZC5wcm90b3R5cGUKQy5qbj1KLmJVLnByb3RvdHlwZQpDLkNEPUoucUkucHJvdG90eXBlCkMu
-eEI9Si5Eci5wcm90b3R5cGUKQy5ERz1KLmM1LnByb3RvdHlwZQpDLkV4PVcudzcucHJvdG90eXBlCkMu
-TkE9SC5WNi5wcm90b3R5cGUKQy50NT1XLkJILnByb3RvdHlwZQpDLkx0PVcuU04ucHJvdG90eXBlCkMu
-WlE9Si5pQy5wcm90b3R5cGUKQy5JZT1XLlRiLnByb3RvdHlwZQpDLnZCPUoua2QucHJvdG90eXBlCkMu
-b2w9Vy5LNS5wcm90b3R5cGUKQy55OD1uZXcgUC5VOCgpCkMuaDk9bmV3IFAuQ1YoKQpDLkd3PW5ldyBI
-LkZ1KEguTjAoIkZ1PDAmKj4iKSkKQy5PND1mdW5jdGlvbiBnZXRUYWdGYWxsYmFjayhvKSB7CiAgdmFy
-IHMgPSBPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmNhbGwobyk7CiAgcmV0dXJuIHMuc3Vic3RyaW5n
-KDgsIHMubGVuZ3RoIC0gMSk7Cn0KQy5ZcT1mdW5jdGlvbigpIHsKICB2YXIgdG9TdHJpbmdGdW5jdGlv
-biA9IE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmc7CiAgZnVuY3Rpb24gZ2V0VGFnKG8pIHsKICAgIHZh
-ciBzID0gdG9TdHJpbmdGdW5jdGlvbi5jYWxsKG8pOwogICAgcmV0dXJuIHMuc3Vic3RyaW5nKDgsIHMu
-bGVuZ3RoIC0gMSk7CiAgfQogIGZ1bmN0aW9uIGdldFVua25vd25UYWcob2JqZWN0LCB0YWcpIHsKICAg
-IGlmICgvXkhUTUxbQS1aXS4qRWxlbWVudCQvLnRlc3QodGFnKSkgewogICAgICB2YXIgbmFtZSA9IHRv
-U3RyaW5nRnVuY3Rpb24uY2FsbChvYmplY3QpOwogICAgICBpZiAobmFtZSA9PSAiW29iamVjdCBPYmpl
-Y3RdIikgcmV0dXJuIG51bGw7CiAgICAgIHJldHVybiAiSFRNTEVsZW1lbnQiOwogICAgfQogIH0KICBm
-dW5jdGlvbiBnZXRVbmtub3duVGFnR2VuZXJpY0Jyb3dzZXIob2JqZWN0LCB0YWcpIHsKICAgIGlmIChz
-ZWxmLkhUTUxFbGVtZW50ICYmIG9iamVjdCBpbnN0YW5jZW9mIEhUTUxFbGVtZW50KSByZXR1cm4gIkhU
-TUxFbGVtZW50IjsKICAgIHJldHVybiBnZXRVbmtub3duVGFnKG9iamVjdCwgdGFnKTsKICB9CiAgZnVu
-Y3Rpb24gcHJvdG90eXBlRm9yVGFnKHRhZykgewogICAgaWYgKHR5cGVvZiB3aW5kb3cgPT0gInVuZGVm
-aW5lZCIpIHJldHVybiBudWxsOwogICAgaWYgKHR5cGVvZiB3aW5kb3dbdGFnXSA9PSAidW5kZWZpbmVk
-IikgcmV0dXJuIG51bGw7CiAgICB2YXIgY29uc3RydWN0b3IgPSB3aW5kb3dbdGFnXTsKICAgIGlmICh0
-eXBlb2YgY29uc3RydWN0b3IgIT0gImZ1bmN0aW9uIikgcmV0dXJuIG51bGw7CiAgICByZXR1cm4gY29u
-c3RydWN0b3IucHJvdG90eXBlOwogIH0KICBmdW5jdGlvbiBkaXNjcmltaW5hdG9yKHRhZykgeyByZXR1
-cm4gbnVsbDsgfQogIHZhciBpc0Jyb3dzZXIgPSB0eXBlb2YgbmF2aWdhdG9yID09ICJvYmplY3QiOwog
-IHJldHVybiB7CiAgICBnZXRUYWc6IGdldFRhZywKICAgIGdldFVua25vd25UYWc6IGlzQnJvd3NlciA/
-IGdldFVua25vd25UYWdHZW5lcmljQnJvd3NlciA6IGdldFVua25vd25UYWcsCiAgICBwcm90b3R5cGVG
-b3JUYWc6IHByb3RvdHlwZUZvclRhZywKICAgIGRpc2NyaW1pbmF0b3I6IGRpc2NyaW1pbmF0b3IgfTsK
-fQpDLndiPWZ1bmN0aW9uKGdldFRhZ0ZhbGxiYWNrKSB7CiAgcmV0dXJuIGZ1bmN0aW9uKGhvb2tzKSB7
-CiAgICBpZiAodHlwZW9mIG5hdmlnYXRvciAhPSAib2JqZWN0IikgcmV0dXJuIGhvb2tzOwogICAgdmFy
-IHVhID0gbmF2aWdhdG9yLnVzZXJBZ2VudDsKICAgIGlmICh1YS5pbmRleE9mKCJEdW1wUmVuZGVyVHJl
-ZSIpID49IDApIHJldHVybiBob29rczsKICAgIGlmICh1YS5pbmRleE9mKCJDaHJvbWUiKSA+PSAwKSB7
-CiAgICAgIGZ1bmN0aW9uIGNvbmZpcm0ocCkgewogICAgICAgIHJldHVybiB0eXBlb2Ygd2luZG93ID09
-ICJvYmplY3QiICYmIHdpbmRvd1twXSAmJiB3aW5kb3dbcF0ubmFtZSA9PSBwOwogICAgICB9CiAgICAg
-IGlmIChjb25maXJtKCJXaW5kb3ciKSAmJiBjb25maXJtKCJIVE1MRWxlbWVudCIpKSByZXR1cm4gaG9v
-a3M7CiAgICB9CiAgICBob29rcy5nZXRUYWcgPSBnZXRUYWdGYWxsYmFjazsKICB9Owp9CkMuS1U9ZnVu
-Y3Rpb24oaG9va3MpIHsKICBpZiAodHlwZW9mIGRhcnRFeHBlcmltZW50YWxGaXh1cEdldFRhZyAhPSAi
-ZnVuY3Rpb24iKSByZXR1cm4gaG9va3M7CiAgaG9va3MuZ2V0VGFnID0gZGFydEV4cGVyaW1lbnRhbEZp
-eHVwR2V0VGFnKGhvb2tzLmdldFRhZyk7Cn0KQy5mUT1mdW5jdGlvbihob29rcykgewogIHZhciBnZXRU
-YWcgPSBob29rcy5nZXRUYWc7CiAgdmFyIHByb3RvdHlwZUZvclRhZyA9IGhvb2tzLnByb3RvdHlwZUZv
-clRhZzsKICBmdW5jdGlvbiBnZXRUYWdGaXhlZChvKSB7CiAgICB2YXIgdGFnID0gZ2V0VGFnKG8pOwog
-ICAgaWYgKHRhZyA9PSAiRG9jdW1lbnQiKSB7CiAgICAgIGlmICghIW8ueG1sVmVyc2lvbikgcmV0dXJu
-ICIhRG9jdW1lbnQiOwogICAgICByZXR1cm4gIiFIVE1MRG9jdW1lbnQiOwogICAgfQogICAgcmV0dXJu
-IHRhZzsKICB9CiAgZnVuY3Rpb24gcHJvdG90eXBlRm9yVGFnRml4ZWQodGFnKSB7CiAgICBpZiAodGFn
-ID09ICJEb2N1bWVudCIpIHJldHVybiBudWxsOwogICAgcmV0dXJuIHByb3RvdHlwZUZvclRhZyh0YWcp
-OwogIH0KICBob29rcy5nZXRUYWcgPSBnZXRUYWdGaXhlZDsKICBob29rcy5wcm90b3R5cGVGb3JUYWcg
-PSBwcm90b3R5cGVGb3JUYWdGaXhlZDsKfQpDLmRrPWZ1bmN0aW9uKGhvb2tzKSB7CiAgdmFyIHVzZXJB
-Z2VudCA9IHR5cGVvZiBuYXZpZ2F0b3IgPT0gIm9iamVjdCIgPyBuYXZpZ2F0b3IudXNlckFnZW50IDog
-IiI7CiAgaWYgKHVzZXJBZ2VudC5pbmRleE9mKCJGaXJlZm94IikgPT0gLTEpIHJldHVybiBob29rczsK
-ICB2YXIgZ2V0VGFnID0gaG9va3MuZ2V0VGFnOwogIHZhciBxdWlja01hcCA9IHsKICAgICJCZWZvcmVV
-bmxvYWRFdmVudCI6ICJFdmVudCIsCiAgICAiRGF0YVRyYW5zZmVyIjogIkNsaXBib2FyZCIsCiAgICAi
-R2VvR2VvbG9jYXRpb24iOiAiR2VvbG9jYXRpb24iLAogICAgIkxvY2F0aW9uIjogIiFMb2NhdGlvbiIs
-CiAgICAiV29ya2VyTWVzc2FnZUV2ZW50IjogIk1lc3NhZ2VFdmVudCIsCiAgICAiWE1MRG9jdW1lbnQi
-OiAiIURvY3VtZW50In07CiAgZnVuY3Rpb24gZ2V0VGFnRmlyZWZveChvKSB7CiAgICB2YXIgdGFnID0g
-Z2V0VGFnKG8pOwogICAgcmV0dXJuIHF1aWNrTWFwW3RhZ10gfHwgdGFnOwogIH0KICBob29rcy5nZXRU
-YWcgPSBnZXRUYWdGaXJlZm94Owp9CkMueGk9ZnVuY3Rpb24oaG9va3MpIHsKICB2YXIgdXNlckFnZW50
-ID0gdHlwZW9mIG5hdmlnYXRvciA9PSAib2JqZWN0IiA/IG5hdmlnYXRvci51c2VyQWdlbnQgOiAiIjsK
-ICBpZiAodXNlckFnZW50LmluZGV4T2YoIlRyaWRlbnQvIikgPT0gLTEpIHJldHVybiBob29rczsKICB2
-YXIgZ2V0VGFnID0gaG9va3MuZ2V0VGFnOwogIHZhciBxdWlja01hcCA9IHsKICAgICJCZWZvcmVVbmxv
-YWRFdmVudCI6ICJFdmVudCIsCiAgICAiRGF0YVRyYW5zZmVyIjogIkNsaXBib2FyZCIsCiAgICAiSFRN
-TERERWxlbWVudCI6ICJIVE1MRWxlbWVudCIsCiAgICAiSFRNTERURWxlbWVudCI6ICJIVE1MRWxlbWVu
-dCIsCiAgICAiSFRNTFBocmFzZUVsZW1lbnQiOiAiSFRNTEVsZW1lbnQiLAogICAgIlBvc2l0aW9uIjog
-Ikdlb3Bvc2l0aW9uIgogIH07CiAgZnVuY3Rpb24gZ2V0VGFnSUUobykgewogICAgdmFyIHRhZyA9IGdl
-dFRhZyhvKTsKICAgIHZhciBuZXdUYWcgPSBxdWlja01hcFt0YWddOwogICAgaWYgKG5ld1RhZykgcmV0
-dXJuIG5ld1RhZzsKICAgIGlmICh0YWcgPT0gIk9iamVjdCIpIHsKICAgICAgaWYgKHdpbmRvdy5EYXRh
-VmlldyAmJiAobyBpbnN0YW5jZW9mIHdpbmRvdy5EYXRhVmlldykpIHJldHVybiAiRGF0YVZpZXciOwog
-ICAgfQogICAgcmV0dXJuIHRhZzsKICB9CiAgZnVuY3Rpb24gcHJvdG90eXBlRm9yVGFnSUUodGFnKSB7
-CiAgICB2YXIgY29uc3RydWN0b3IgPSB3aW5kb3dbdGFnXTsKICAgIGlmIChjb25zdHJ1Y3RvciA9PSBu
-dWxsKSByZXR1cm4gbnVsbDsKICAgIHJldHVybiBjb25zdHJ1Y3Rvci5wcm90b3R5cGU7CiAgfQogIGhv
-b2tzLmdldFRhZyA9IGdldFRhZ0lFOwogIGhvb2tzLnByb3RvdHlwZUZvclRhZyA9IHByb3RvdHlwZUZv
-clRhZ0lFOwp9CkMuaTc9ZnVuY3Rpb24oaG9va3MpIHsgcmV0dXJuIGhvb2tzOyB9CgpDLkN0PW5ldyBQ
-LmJ5KCkKQy5FcT1uZXcgUC5rNSgpCkMueE09bmV3IFAudTUoKQpDLlFrPW5ldyBQLkUzKCkKQy5Odj1u
-ZXcgSC5rcigpCkMuTlU9bmV3IFAuSmkoKQpDLnBkPW5ldyBQLlpkKCkKQy5BZD1uZXcgUi5INygwLCJI
-aW50QWN0aW9uS2luZC5hZGROdWxsYWJsZUhpbnQiKQpDLm5lPW5ldyBSLkg3KDEsIkhpbnRBY3Rpb25L
-aW5kLmFkZE5vbk51bGxhYmxlSGludCIpCkMubXk9bmV3IFIuSDcoMiwiSGludEFjdGlvbktpbmQuY2hh
-bmdlVG9OdWxsYWJsZUhpbnQiKQpDLnJ4PW5ldyBSLkg3KDMsIkhpbnRBY3Rpb25LaW5kLmNoYW5nZVRv
-Tm9uTnVsbGFibGVIaW50IikKQy53Vj1uZXcgUi5INyg0LCJIaW50QWN0aW9uS2luZC5yZW1vdmVOdWxs
-YWJsZUhpbnQiKQpDLmZSPW5ldyBSLkg3KDUsIkhpbnRBY3Rpb25LaW5kLnJlbW92ZU5vbk51bGxhYmxl
-SGludCIpCkMuQTM9bmV3IFAuTXgobnVsbCkKQy5uWD1uZXcgUC5vaihudWxsKQpDLmN3PW5ldyBMLkdi
-KDAsIlVuaXRNaWdyYXRpb25TdGF0dXMuYWxyZWFkeU1pZ3JhdGVkIikKQy5kYz1uZXcgTC5HYigxLCJV
-bml0TWlncmF0aW9uU3RhdHVzLmluZGV0ZXJtaW5hdGUiKQpDLldEPW5ldyBMLkdiKDIsIlVuaXRNaWdy
-YXRpb25TdGF0dXMubWlncmF0aW5nIikKQy5Yaj1uZXcgTC5HYigzLCJVbml0TWlncmF0aW9uU3RhdHVz
-Lm9wdGluZ091dCIpCkMubDA9SC5RSShzKFtDLmN3LEMuZGMsQy5XRCxDLlhqXSksSC5OMCgiamQ8R2Iq
-PiIpKQpDLmFrPUguUUkocyhbMCwwLDMyNzc2LDMzNzkyLDEsMTAyNDAsMCwwXSksdC5WKQpDLmNtPUgu
-UUkocyhbIio6OmNsYXNzIiwiKjo6ZGlyIiwiKjo6ZHJhZ2dhYmxlIiwiKjo6aGlkZGVuIiwiKjo6aWQi
-LCIqOjppbmVydCIsIio6Oml0ZW1wcm9wIiwiKjo6aXRlbXJlZiIsIio6Oml0ZW1zY29wZSIsIio6Omxh
-bmciLCIqOjpzcGVsbGNoZWNrIiwiKjo6dGl0bGUiLCIqOjp0cmFuc2xhdGUiLCJBOjphY2Nlc3NrZXki
-LCJBOjpjb29yZHMiLCJBOjpocmVmbGFuZyIsIkE6Om5hbWUiLCJBOjpzaGFwZSIsIkE6OnRhYmluZGV4
-IiwiQTo6dGFyZ2V0IiwiQTo6dHlwZSIsIkFSRUE6OmFjY2Vzc2tleSIsIkFSRUE6OmFsdCIsIkFSRUE6
-OmNvb3JkcyIsIkFSRUE6Om5vaHJlZiIsIkFSRUE6OnNoYXBlIiwiQVJFQTo6dGFiaW5kZXgiLCJBUkVB
-Ojp0YXJnZXQiLCJBVURJTzo6Y29udHJvbHMiLCJBVURJTzo6bG9vcCIsIkFVRElPOjptZWRpYWdyb3Vw
-IiwiQVVESU86Om11dGVkIiwiQVVESU86OnByZWxvYWQiLCJCRE86OmRpciIsIkJPRFk6OmFsaW5rIiwi
-Qk9EWTo6Ymdjb2xvciIsIkJPRFk6OmxpbmsiLCJCT0RZOjp0ZXh0IiwiQk9EWTo6dmxpbmsiLCJCUjo6
-Y2xlYXIiLCJCVVRUT046OmFjY2Vzc2tleSIsIkJVVFRPTjo6ZGlzYWJsZWQiLCJCVVRUT046Om5hbWUi
-LCJCVVRUT046OnRhYmluZGV4IiwiQlVUVE9OOjp0eXBlIiwiQlVUVE9OOjp2YWx1ZSIsIkNBTlZBUzo6
-aGVpZ2h0IiwiQ0FOVkFTOjp3aWR0aCIsIkNBUFRJT046OmFsaWduIiwiQ09MOjphbGlnbiIsIkNPTDo6
-Y2hhciIsIkNPTDo6Y2hhcm9mZiIsIkNPTDo6c3BhbiIsIkNPTDo6dmFsaWduIiwiQ09MOjp3aWR0aCIs
-IkNPTEdST1VQOjphbGlnbiIsIkNPTEdST1VQOjpjaGFyIiwiQ09MR1JPVVA6OmNoYXJvZmYiLCJDT0xH
-Uk9VUDo6c3BhbiIsIkNPTEdST1VQOjp2YWxpZ24iLCJDT0xHUk9VUDo6d2lkdGgiLCJDT01NQU5EOjpj
-aGVja2VkIiwiQ09NTUFORDo6Y29tbWFuZCIsIkNPTU1BTkQ6OmRpc2FibGVkIiwiQ09NTUFORDo6bGFi
-ZWwiLCJDT01NQU5EOjpyYWRpb2dyb3VwIiwiQ09NTUFORDo6dHlwZSIsIkRBVEE6OnZhbHVlIiwiREVM
-OjpkYXRldGltZSIsIkRFVEFJTFM6Om9wZW4iLCJESVI6OmNvbXBhY3QiLCJESVY6OmFsaWduIiwiREw6
-OmNvbXBhY3QiLCJGSUVMRFNFVDo6ZGlzYWJsZWQiLCJGT05UOjpjb2xvciIsIkZPTlQ6OmZhY2UiLCJG
-T05UOjpzaXplIiwiRk9STTo6YWNjZXB0IiwiRk9STTo6YXV0b2NvbXBsZXRlIiwiRk9STTo6ZW5jdHlw
-ZSIsIkZPUk06Om1ldGhvZCIsIkZPUk06Om5hbWUiLCJGT1JNOjpub3ZhbGlkYXRlIiwiRk9STTo6dGFy
-Z2V0IiwiRlJBTUU6Om5hbWUiLCJIMTo6YWxpZ24iLCJIMjo6YWxpZ24iLCJIMzo6YWxpZ24iLCJINDo6
-YWxpZ24iLCJINTo6YWxpZ24iLCJINjo6YWxpZ24iLCJIUjo6YWxpZ24iLCJIUjo6bm9zaGFkZSIsIkhS
-OjpzaXplIiwiSFI6OndpZHRoIiwiSFRNTDo6dmVyc2lvbiIsIklGUkFNRTo6YWxpZ24iLCJJRlJBTUU6
-OmZyYW1lYm9yZGVyIiwiSUZSQU1FOjpoZWlnaHQiLCJJRlJBTUU6Om1hcmdpbmhlaWdodCIsIklGUkFN
-RTo6bWFyZ2lud2lkdGgiLCJJRlJBTUU6OndpZHRoIiwiSU1HOjphbGlnbiIsIklNRzo6YWx0IiwiSU1H
-Ojpib3JkZXIiLCJJTUc6OmhlaWdodCIsIklNRzo6aHNwYWNlIiwiSU1HOjppc21hcCIsIklNRzo6bmFt
-ZSIsIklNRzo6dXNlbWFwIiwiSU1HOjp2c3BhY2UiLCJJTUc6OndpZHRoIiwiSU5QVVQ6OmFjY2VwdCIs
-IklOUFVUOjphY2Nlc3NrZXkiLCJJTlBVVDo6YWxpZ24iLCJJTlBVVDo6YWx0IiwiSU5QVVQ6OmF1dG9j
-b21wbGV0ZSIsIklOUFVUOjphdXRvZm9jdXMiLCJJTlBVVDo6Y2hlY2tlZCIsIklOUFVUOjpkaXNhYmxl
-ZCIsIklOUFVUOjppbnB1dG1vZGUiLCJJTlBVVDo6aXNtYXAiLCJJTlBVVDo6bGlzdCIsIklOUFVUOjpt
-YXgiLCJJTlBVVDo6bWF4bGVuZ3RoIiwiSU5QVVQ6Om1pbiIsIklOUFVUOjptdWx0aXBsZSIsIklOUFVU
-OjpuYW1lIiwiSU5QVVQ6OnBsYWNlaG9sZGVyIiwiSU5QVVQ6OnJlYWRvbmx5IiwiSU5QVVQ6OnJlcXVp
-cmVkIiwiSU5QVVQ6OnNpemUiLCJJTlBVVDo6c3RlcCIsIklOUFVUOjp0YWJpbmRleCIsIklOUFVUOjp0
-eXBlIiwiSU5QVVQ6OnVzZW1hcCIsIklOUFVUOjp2YWx1ZSIsIklOUzo6ZGF0ZXRpbWUiLCJLRVlHRU46
-OmRpc2FibGVkIiwiS0VZR0VOOjprZXl0eXBlIiwiS0VZR0VOOjpuYW1lIiwiTEFCRUw6OmFjY2Vzc2tl
-eSIsIkxBQkVMOjpmb3IiLCJMRUdFTkQ6OmFjY2Vzc2tleSIsIkxFR0VORDo6YWxpZ24iLCJMSTo6dHlw
-ZSIsIkxJOjp2YWx1ZSIsIkxJTks6OnNpemVzIiwiTUFQOjpuYW1lIiwiTUVOVTo6Y29tcGFjdCIsIk1F
-TlU6OmxhYmVsIiwiTUVOVTo6dHlwZSIsIk1FVEVSOjpoaWdoIiwiTUVURVI6OmxvdyIsIk1FVEVSOjpt
-YXgiLCJNRVRFUjo6bWluIiwiTUVURVI6OnZhbHVlIiwiT0JKRUNUOjp0eXBlbXVzdG1hdGNoIiwiT0w6
-OmNvbXBhY3QiLCJPTDo6cmV2ZXJzZWQiLCJPTDo6c3RhcnQiLCJPTDo6dHlwZSIsIk9QVEdST1VQOjpk
-aXNhYmxlZCIsIk9QVEdST1VQOjpsYWJlbCIsIk9QVElPTjo6ZGlzYWJsZWQiLCJPUFRJT046OmxhYmVs
-IiwiT1BUSU9OOjpzZWxlY3RlZCIsIk9QVElPTjo6dmFsdWUiLCJPVVRQVVQ6OmZvciIsIk9VVFBVVDo6
-bmFtZSIsIlA6OmFsaWduIiwiUFJFOjp3aWR0aCIsIlBST0dSRVNTOjptYXgiLCJQUk9HUkVTUzo6bWlu
-IiwiUFJPR1JFU1M6OnZhbHVlIiwiU0VMRUNUOjphdXRvY29tcGxldGUiLCJTRUxFQ1Q6OmRpc2FibGVk
-IiwiU0VMRUNUOjptdWx0aXBsZSIsIlNFTEVDVDo6bmFtZSIsIlNFTEVDVDo6cmVxdWlyZWQiLCJTRUxF
-Q1Q6OnNpemUiLCJTRUxFQ1Q6OnRhYmluZGV4IiwiU09VUkNFOjp0eXBlIiwiVEFCTEU6OmFsaWduIiwi
-VEFCTEU6OmJnY29sb3IiLCJUQUJMRTo6Ym9yZGVyIiwiVEFCTEU6OmNlbGxwYWRkaW5nIiwiVEFCTEU6
-OmNlbGxzcGFjaW5nIiwiVEFCTEU6OmZyYW1lIiwiVEFCTEU6OnJ1bGVzIiwiVEFCTEU6OnN1bW1hcnki
-LCJUQUJMRTo6d2lkdGgiLCJUQk9EWTo6YWxpZ24iLCJUQk9EWTo6Y2hhciIsIlRCT0RZOjpjaGFyb2Zm
-IiwiVEJPRFk6OnZhbGlnbiIsIlREOjphYmJyIiwiVEQ6OmFsaWduIiwiVEQ6OmF4aXMiLCJURDo6Ymdj
-b2xvciIsIlREOjpjaGFyIiwiVEQ6OmNoYXJvZmYiLCJURDo6Y29sc3BhbiIsIlREOjpoZWFkZXJzIiwi
-VEQ6OmhlaWdodCIsIlREOjpub3dyYXAiLCJURDo6cm93c3BhbiIsIlREOjpzY29wZSIsIlREOjp2YWxp
-Z24iLCJURDo6d2lkdGgiLCJURVhUQVJFQTo6YWNjZXNza2V5IiwiVEVYVEFSRUE6OmF1dG9jb21wbGV0
-ZSIsIlRFWFRBUkVBOjpjb2xzIiwiVEVYVEFSRUE6OmRpc2FibGVkIiwiVEVYVEFSRUE6OmlucHV0bW9k
-ZSIsIlRFWFRBUkVBOjpuYW1lIiwiVEVYVEFSRUE6OnBsYWNlaG9sZGVyIiwiVEVYVEFSRUE6OnJlYWRv
-bmx5IiwiVEVYVEFSRUE6OnJlcXVpcmVkIiwiVEVYVEFSRUE6OnJvd3MiLCJURVhUQVJFQTo6dGFiaW5k
-ZXgiLCJURVhUQVJFQTo6d3JhcCIsIlRGT09UOjphbGlnbiIsIlRGT09UOjpjaGFyIiwiVEZPT1Q6OmNo
-YXJvZmYiLCJURk9PVDo6dmFsaWduIiwiVEg6OmFiYnIiLCJUSDo6YWxpZ24iLCJUSDo6YXhpcyIsIlRI
-OjpiZ2NvbG9yIiwiVEg6OmNoYXIiLCJUSDo6Y2hhcm9mZiIsIlRIOjpjb2xzcGFuIiwiVEg6OmhlYWRl
-cnMiLCJUSDo6aGVpZ2h0IiwiVEg6Om5vd3JhcCIsIlRIOjpyb3dzcGFuIiwiVEg6OnNjb3BlIiwiVEg6
-OnZhbGlnbiIsIlRIOjp3aWR0aCIsIlRIRUFEOjphbGlnbiIsIlRIRUFEOjpjaGFyIiwiVEhFQUQ6OmNo
-YXJvZmYiLCJUSEVBRDo6dmFsaWduIiwiVFI6OmFsaWduIiwiVFI6OmJnY29sb3IiLCJUUjo6Y2hhciIs
-IlRSOjpjaGFyb2ZmIiwiVFI6OnZhbGlnbiIsIlRSQUNLOjpkZWZhdWx0IiwiVFJBQ0s6OmtpbmQiLCJU
-UkFDSzo6bGFiZWwiLCJUUkFDSzo6c3JjbGFuZyIsIlVMOjpjb21wYWN0IiwiVUw6OnR5cGUiLCJWSURF
-Tzo6Y29udHJvbHMiLCJWSURFTzo6aGVpZ2h0IiwiVklERU86Omxvb3AiLCJWSURFTzo6bWVkaWFncm91
-cCIsIlZJREVPOjptdXRlZCIsIlZJREVPOjpwcmVsb2FkIiwiVklERU86OndpZHRoIl0pLHQuaSkKQy5W
-Qz1ILlFJKHMoWzAsMCw2NTQ5MCw0NTA1NSw2NTUzNSwzNDgxNSw2NTUzNCwxODQzMV0pLHQuVikKQy5t
-Sz1ILlFJKHMoWzAsMCwyNjYyNCwxMDIzLDY1NTM0LDIwNDcsNjU1MzQsMjA0N10pLHQuVikKQy5TcT1I
-LlFJKHMoWyJIRUFEIiwiQVJFQSIsIkJBU0UiLCJCQVNFRk9OVCIsIkJSIiwiQ09MIiwiQ09MR1JPVVAi
-LCJFTUJFRCIsIkZSQU1FIiwiRlJBTUVTRVQiLCJIUiIsIklNQUdFIiwiSU1HIiwiSU5QVVQiLCJJU0lO
-REVYIiwiTElOSyIsIk1FVEEiLCJQQVJBTSIsIlNPVVJDRSIsIlNUWUxFIiwiVElUTEUiLCJXQlIiXSks
-dC5pKQpDLmhVPUguUUkocyhbXSksdC5iKQpDLmRuPUguUUkocyhbXSksSC5OMCgiamQ8TEwqPiIpKQpD
-LnhEPUguUUkocyhbXSksdC5pKQpDLnRvPUguUUkocyhbMCwwLDMyNzIyLDEyMjg3LDY1NTM0LDM0ODE1
-LDY1NTM0LDE4NDMxXSksdC5WKQpDLnJrPUguUUkocyhbQy5BZCxDLm5lLEMubXksQy5yeCxDLndWLEMu
-ZlJdKSxILk4wKCJqZDxINyo+IikpCkMuRjM9SC5RSShzKFswLDAsMjQ1NzYsMTAyMyw2NTUzNCwzNDgx
-NSw2NTUzNCwxODQzMV0pLHQuVikKQy5lYT1ILlFJKHMoWzAsMCwzMjc1NCwxMTI2Myw2NTUzNCwzNDgx
-NSw2NTUzNCwxODQzMV0pLHQuVikKQy5aSj1ILlFJKHMoWzAsMCwzMjcyMiwxMjI4Nyw2NTUzNSwzNDgx
-NSw2NTUzNCwxODQzMV0pLHQuVikKQy5XZD1ILlFJKHMoWzAsMCw2NTQ5MCwxMjI4Nyw2NTUzNSwzNDgx
-NSw2NTUzNCwxODQzMV0pLHQuVikKQy5ReD1ILlFJKHMoWyJiaW5kIiwiaWYiLCJyZWYiLCJyZXBlYXQi
-LCJzeW50YXgiXSksdC5pKQpDLkJJPUguUUkocyhbIkE6OmhyZWYiLCJBUkVBOjpocmVmIiwiQkxPQ0tR
-VU9URTo6Y2l0ZSIsIkJPRFk6OmJhY2tncm91bmQiLCJDT01NQU5EOjppY29uIiwiREVMOjpjaXRlIiwi
-Rk9STTo6YWN0aW9uIiwiSU1HOjpzcmMiLCJJTlBVVDo6c3JjIiwiSU5TOjpjaXRlIiwiUTo6Y2l0ZSIs
-IlZJREVPOjpwb3N0ZXIiXSksdC5pKQpDLkR4PW5ldyBILkxQKDAse30sQy54RCxILk4wKCJMUDxxVSos
-ek08ajgqPio+IikpCkMuQ009bmV3IEguTFAoMCx7fSxDLnhELEguTjAoIkxQPHFVKixxVSo+IikpCkMu
-aUg9SC5RSShzKFtdKSxILk4wKCJqZDxHRCo+IikpCkMuV089bmV3IEguTFAoMCx7fSxDLmlILEguTjAo
-IkxQPEdEKixAPiIpKQpDLlkyPW5ldyBMLk85KCJOYXZpZ2F0aW9uVHJlZU5vZGVUeXBlLmRpcmVjdG9y
-eSIpCkMucmY9bmV3IEwuTzkoIk5hdmlnYXRpb25UcmVlTm9kZVR5cGUuZmlsZSIpCkMuVGU9bmV3IEgu
-d3YoImNhbGwiKQpDLm9FPW5ldyBQLkdZKCExKQpDLndRPW5ldyBQLkZ5KG51bGwsMil9KSgpOyhmdW5j
-dGlvbiBzdGF0aWNGaWVsZHMoKXskLnptPW51bGwKJC55aj0wCiQuV1c9bnVsbAokLmkwPW51bGwKJC5O
-Rj1udWxsCiQuVFg9bnVsbAokLng3PW51bGwKJC5udz1udWxsCiQudnY9bnVsbAokLkJ2PW51bGwKJC5T
-Nj1udWxsCiQuazg9bnVsbAokLm1nPW51bGwKJC5VRD0hMQokLlgzPUMuTlUKJC5GPUguUUkoW10sSC5O
-MCgiamQ8TWg+IikpCiQueG89bnVsbAokLkJPPW51bGwKJC5sdD1udWxsCiQuRVU9bnVsbAokLm9yPVAu
-RmwodC5OLHQuWSkKJC5JUj1udWxsCiQuSTY9bnVsbAokLkZmPW51bGx9KSgpOyhmdW5jdGlvbiBsYXp5
-SW5pdGlhbGl6ZXJzKCl7dmFyIHM9aHVua0hlbHBlcnMubGF6eUZpbmFsLHI9aHVua0hlbHBlcnMubGF6
-eU9sZApzKCQsImZhIiwieiIsZnVuY3Rpb24oKXtyZXR1cm4gSC5ZZygiXyRkYXJ0X2RhcnRDbG9zdXJl
-Iil9KQpzKCQsIktxIiwiU24iLGZ1bmN0aW9uKCl7cmV0dXJuIEguY00oSC5TNyh7CnRvU3RyaW5nOmZ1
-bmN0aW9uKCl7cmV0dXJuIiRyZWNlaXZlciQifX0pKX0pCnMoJCwieHEiLCJscSIsZnVuY3Rpb24oKXty
-ZXR1cm4gSC5jTShILlM3KHskbWV0aG9kJDpudWxsLAp0b1N0cmluZzpmdW5jdGlvbigpe3JldHVybiIk
-cmVjZWl2ZXIkIn19KSl9KQpzKCQsIlIxIiwiTjkiLGZ1bmN0aW9uKCl7cmV0dXJuIEguY00oSC5TNyhu
-dWxsKSl9KQpzKCQsImZOIiwiaUkiLGZ1bmN0aW9uKCl7cmV0dXJuIEguY00oZnVuY3Rpb24oKXt2YXIg
-JGFyZ3VtZW50c0V4cHIkPSIkYXJndW1lbnRzJCIKdHJ5e251bGwuJG1ldGhvZCQoJGFyZ3VtZW50c0V4
-cHIkKX1jYXRjaChxKXtyZXR1cm4gcS5tZXNzYWdlfX0oKSl9KQpzKCQsInFpIiwiVU4iLGZ1bmN0aW9u
-KCl7cmV0dXJuIEguY00oSC5TNyh2b2lkIDApKX0pCnMoJCwicHYiLCJaaCIsZnVuY3Rpb24oKXtyZXR1
-cm4gSC5jTShmdW5jdGlvbigpe3ZhciAkYXJndW1lbnRzRXhwciQ9IiRhcmd1bWVudHMkIgp0cnl7KHZv
-aWQgMCkuJG1ldGhvZCQoJGFyZ3VtZW50c0V4cHIkKX1jYXRjaChxKXtyZXR1cm4gcS5tZXNzYWdlfX0o
-KSl9KQpzKCQsImtxIiwick4iLGZ1bmN0aW9uKCl7cmV0dXJuIEguY00oSC5NaihudWxsKSl9KQpzKCQs
-InR0IiwiYzMiLGZ1bmN0aW9uKCl7cmV0dXJuIEguY00oZnVuY3Rpb24oKXt0cnl7bnVsbC4kbWV0aG9k
-JH1jYXRjaChxKXtyZXR1cm4gcS5tZXNzYWdlfX0oKSl9KQpzKCQsImR0IiwiSEsiLGZ1bmN0aW9uKCl7
-cmV0dXJuIEguY00oSC5Naih2b2lkIDApKX0pCnMoJCwiQTciLCJyMSIsZnVuY3Rpb24oKXtyZXR1cm4g
-SC5jTShmdW5jdGlvbigpe3RyeXsodm9pZCAwKS4kbWV0aG9kJH1jYXRjaChxKXtyZXR1cm4gcS5tZXNz
-YWdlfX0oKSl9KQpzKCQsIldjIiwidXQiLGZ1bmN0aW9uKCl7cmV0dXJuIFAueGcoKX0pCnMoJCwia2gi
-LCJyZiIsZnVuY3Rpb24oKXtyZXR1cm4gbmV3IFAueHIoKS4kMCgpfSkKcygkLCJkSCIsIkhHIixmdW5j
-dGlvbigpe3JldHVybiBuZXcgUC5OeigpLiQwKCl9KQpzKCQsImhqIiwiVjciLGZ1bmN0aW9uKCl7cmV0
-dXJuIG5ldyBJbnQ4QXJyYXkoSC5YRihILlFJKFstMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwt
-MiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwtMiwt
-MiwtMiwtMiwtMiwtMiwtMiwtMiwtMSwtMiwtMiwtMiwtMiwtMiw2MiwtMiw2MiwtMiw2Myw1Miw1Myw1
-NCw1NSw1Niw1Nyw1OCw1OSw2MCw2MSwtMiwtMiwtMiwtMSwtMiwtMiwtMiwwLDEsMiwzLDQsNSw2LDcs
-OCw5LDEwLDExLDEyLDEzLDE0LDE1LDE2LDE3LDE4LDE5LDIwLDIxLDIyLDIzLDI0LDI1LC0yLC0yLC0y
-LC0yLDYzLC0yLDI2LDI3LDI4LDI5LDMwLDMxLDMyLDMzLDM0LDM1LDM2LDM3LDM4LDM5LDQwLDQxLDQy
-LDQzLDQ0LDQ1LDQ2LDQ3LDQ4LDQ5LDUwLDUxLC0yLC0yLC0yLC0yLC0yXSx0LmEpKSl9KQpzKCQsIlll
-Iiwid1EiLGZ1bmN0aW9uKCl7cmV0dXJuIHR5cGVvZiBwcm9jZXNzIT0idW5kZWZpbmVkIiYmT2JqZWN0
-LnByb3RvdHlwZS50b1N0cmluZy5jYWxsKHByb2Nlc3MpPT0iW29iamVjdCBwcm9jZXNzXSImJnByb2Nl
-c3MucGxhdGZvcm09PSJ3aW4zMiJ9KQpzKCQsIm1mIiwiejQiLGZ1bmN0aW9uKCl7cmV0dXJuIFAubnUo
-Il5bXFwtXFwuMC05QS1aX2Eten5dKiQiKX0pCnMoJCwiT1EiLCJ2WiIsZnVuY3Rpb24oKXtyZXR1cm4g
-UC5LTigpfSkKcygkLCJTQyIsIkFOIixmdW5jdGlvbigpe3JldHVybiBQLnRNKFsiQSIsIkFCQlIiLCJB
-Q1JPTllNIiwiQUREUkVTUyIsIkFSRUEiLCJBUlRJQ0xFIiwiQVNJREUiLCJBVURJTyIsIkIiLCJCREki
-LCJCRE8iLCJCSUciLCJCTE9DS1FVT1RFIiwiQlIiLCJCVVRUT04iLCJDQU5WQVMiLCJDQVBUSU9OIiwi
-Q0VOVEVSIiwiQ0lURSIsIkNPREUiLCJDT0wiLCJDT0xHUk9VUCIsIkNPTU1BTkQiLCJEQVRBIiwiREFU
-QUxJU1QiLCJERCIsIkRFTCIsIkRFVEFJTFMiLCJERk4iLCJESVIiLCJESVYiLCJETCIsIkRUIiwiRU0i
-LCJGSUVMRFNFVCIsIkZJR0NBUFRJT04iLCJGSUdVUkUiLCJGT05UIiwiRk9PVEVSIiwiRk9STSIsIkgx
-IiwiSDIiLCJIMyIsIkg0IiwiSDUiLCJINiIsIkhFQURFUiIsIkhHUk9VUCIsIkhSIiwiSSIsIklGUkFN
-RSIsIklNRyIsIklOUFVUIiwiSU5TIiwiS0JEIiwiTEFCRUwiLCJMRUdFTkQiLCJMSSIsIk1BUCIsIk1B
-UksiLCJNRU5VIiwiTUVURVIiLCJOQVYiLCJOT0JSIiwiT0wiLCJPUFRHUk9VUCIsIk9QVElPTiIsIk9V
-VFBVVCIsIlAiLCJQUkUiLCJQUk9HUkVTUyIsIlEiLCJTIiwiU0FNUCIsIlNFQ1RJT04iLCJTRUxFQ1Qi
-LCJTTUFMTCIsIlNPVVJDRSIsIlNQQU4iLCJTVFJJS0UiLCJTVFJPTkciLCJTVUIiLCJTVU1NQVJZIiwi
-U1VQIiwiVEFCTEUiLCJUQk9EWSIsIlREIiwiVEVYVEFSRUEiLCJURk9PVCIsIlRIIiwiVEhFQUQiLCJU
-SU1FIiwiVFIiLCJUUkFDSyIsIlRUIiwiVSIsIlVMIiwiVkFSIiwiVklERU8iLCJXQlIiXSx0Lk4pfSkK
-cygkLCJYNCIsImhHIixmdW5jdGlvbigpe3JldHVybiBQLm51KCJeXFxTKyQiKX0pCnMoJCwid08iLCJv
-dyIsZnVuY3Rpb24oKXtyZXR1cm4gUC5ORChzZWxmKX0pCnMoJCwia3QiLCJSOCIsZnVuY3Rpb24oKXty
-ZXR1cm4gSC5ZZygiXyRkYXJ0X2RhcnRPYmplY3QiKX0pCnMoJCwiZksiLCJrSSIsZnVuY3Rpb24oKXty
-ZXR1cm4gZnVuY3Rpb24gRGFydE9iamVjdChhKXt0aGlzLm89YX19KQpyKCQsInF0IiwiekIiLGZ1bmN0
-aW9uKCl7cmV0dXJuIG5ldyBULm1RKCl9KQpyKCQsIk9sIiwiVUUiLGZ1bmN0aW9uKCl7cmV0dXJuIFAu
-aEsoQy5vbC5nbVcoVy54MygpKS5ocmVmKS5naFkoKS5xKDAsImF1dGhUb2tlbiIpfSkKcigkLCJoVCIs
-InlQIixmdW5jdGlvbigpe3JldHVybiBXLlpyKCkucXVlcnlTZWxlY3RvcigiLmVkaXQtbGlzdCAucGFu
-ZWwtY29udGVudCIpfSkKcigkLCJXNiIsImhMIixmdW5jdGlvbigpe3JldHVybiBXLlpyKCkucXVlcnlT
-ZWxlY3RvcigiLmVkaXQtcGFuZWwgLnBhbmVsLWNvbnRlbnQiKX0pCnIoJCwiVFIiLCJEVyIsZnVuY3Rp
-b24oKXtyZXR1cm4gVy5acigpLnF1ZXJ5U2VsZWN0b3IoImZvb3RlciIpfSkKcigkLCJFWSIsImZpIixm
-dW5jdGlvbigpe3JldHVybiBXLlpyKCkucXVlcnlTZWxlY3RvcigiaGVhZGVyIil9KQpyKCQsImJBIiwi
-YzAiLGZ1bmN0aW9uKCl7cmV0dXJuIFcuWnIoKS5xdWVyeVNlbGVjdG9yKCIjbWlncmF0ZS11bml0LXN0
-YXR1cy1pY29uIil9KQpyKCQsInQwIiwiYk4iLGZ1bmN0aW9uKCl7cmV0dXJuIFcuWnIoKS5xdWVyeVNl
-bGVjdG9yKCIjbWlncmF0ZS11bml0LXN0YXR1cy1pY29uLWxhYmVsIil9KQpyKCQsImF2IiwiRDkiLGZ1
-bmN0aW9uKCl7cmV0dXJuIFcuWnIoKS5xdWVyeVNlbGVjdG9yKCIjdW5pdC1uYW1lIil9KQpyKCQsImZl
-IiwiS0ciLGZ1bmN0aW9uKCl7cmV0dXJuIG5ldyBMLlhBKCl9KQpzKCQsImVvIiwiblUiLGZ1bmN0aW9u
-KCl7cmV0dXJuIG5ldyBNLmxJKCQuSGsoKSl9KQpzKCQsInlyIiwiYkQiLGZ1bmN0aW9uKCl7cmV0dXJu
-IG5ldyBFLk9GKFAubnUoIi8iKSxQLm51KCJbXi9dJCIpLFAubnUoIl4vIikpfSkKcygkLCJNayIsIktr
-IixmdW5jdGlvbigpe3JldHVybiBuZXcgTC5JVihQLm51KCJbL1xcXFxdIiksUC5udSgiW14vXFxcXF0k
-IiksUC5udSgiXihcXFxcXFxcXFteXFxcXF0rXFxcXFteXFxcXC9dK3xbYS16QS1aXTpbL1xcXFxdKSIp
-LFAubnUoIl5bL1xcXFxdKD8hWy9cXFxcXSkiKSl9KQpzKCQsImFrIiwiRWIiLGZ1bmN0aW9uKCl7cmV0
-dXJuIG5ldyBGLnJ1KFAubnUoIi8iKSxQLm51KCIoXlthLXpBLVpdWy0rLmEtekEtWlxcZF0qOi8vfFte
-L10pJCIpLFAubnUoIlthLXpBLVpdWy0rLmEtekEtWlxcZF0qOi8vW14vXSoiKSxQLm51KCJeLyIpKX0p
-CnMoJCwibHMiLCJIayIsZnVuY3Rpb24oKXtyZXR1cm4gTy5SaCgpfSl9KSgpOyhmdW5jdGlvbiBuYXRp
-dmVTdXBwb3J0KCl7IWZ1bmN0aW9uKCl7dmFyIHM9ZnVuY3Rpb24oYSl7dmFyIG09e30KbVthXT0xCnJl
-dHVybiBPYmplY3Qua2V5cyhodW5rSGVscGVycy5jb252ZXJ0VG9GYXN0T2JqZWN0KG0pKVswXX0Kdi5n
-ZXRJc29sYXRlVGFnPWZ1bmN0aW9uKGEpe3JldHVybiBzKCJfX19kYXJ0XyIrYSt2Lmlzb2xhdGVUYWcp
-fQp2YXIgcj0iX19fZGFydF9pc29sYXRlX3RhZ3NfIgp2YXIgcT1PYmplY3Rbcl18fChPYmplY3Rbcl09
-T2JqZWN0LmNyZWF0ZShudWxsKSkKdmFyIHA9Il9aeFl4WCIKZm9yKHZhciBvPTA7O28rKyl7dmFyIG49
-cyhwKyJfIitvKyJfIikKaWYoIShuIGluIHEpKXtxW25dPTEKdi5pc29sYXRlVGFnPW4KYnJlYWt9fXYu
-ZGlzcGF0Y2hQcm9wZXJ0eU5hbWU9di5nZXRJc29sYXRlVGFnKCJkaXNwYXRjaF9yZWNvcmQiKX0oKQpo
-dW5rSGVscGVycy5zZXRPclVwZGF0ZUludGVyY2VwdG9yc0J5VGFnKHtET01FcnJvcjpKLkd2LE1lZGlh
-RXJyb3I6Si5HdixOYXZpZ2F0b3I6Si5HdixOYXZpZ2F0b3JDb25jdXJyZW50SGFyZHdhcmU6Si5HdixO
-YXZpZ2F0b3JVc2VyTWVkaWFFcnJvcjpKLkd2LE92ZXJjb25zdHJhaW5lZEVycm9yOkouR3YsUG9zaXRp
-b25FcnJvcjpKLkd2LEdlb2xvY2F0aW9uUG9zaXRpb25FcnJvcjpKLkd2LFJhbmdlOkouR3YsU1FMRXJy
-b3I6Si5HdixEYXRhVmlldzpILkVULEFycmF5QnVmZmVyVmlldzpILkVULEZsb2F0MzJBcnJheTpILkRn
-LEZsb2F0NjRBcnJheTpILkRnLEludDE2QXJyYXk6SC54aixJbnQzMkFycmF5OkguZEUsSW50OEFycmF5
-OkguWkEsVWludDE2QXJyYXk6SC5kVCxVaW50MzJBcnJheTpILlBxLFVpbnQ4Q2xhbXBlZEFycmF5Okgu
-ZUUsQ2FudmFzUGl4ZWxBcnJheTpILmVFLFVpbnQ4QXJyYXk6SC5WNixIVE1MQXVkaW9FbGVtZW50Olcu
-cUUsSFRNTEJSRWxlbWVudDpXLnFFLEhUTUxCdXR0b25FbGVtZW50OlcucUUsSFRNTENhbnZhc0VsZW1l
-bnQ6Vy5xRSxIVE1MQ29udGVudEVsZW1lbnQ6Vy5xRSxIVE1MRExpc3RFbGVtZW50OlcucUUsSFRNTERh
-dGFFbGVtZW50OlcucUUsSFRNTERhdGFMaXN0RWxlbWVudDpXLnFFLEhUTUxEZXRhaWxzRWxlbWVudDpX
-LnFFLEhUTUxEaWFsb2dFbGVtZW50OlcucUUsSFRNTERpdkVsZW1lbnQ6Vy5xRSxIVE1MRW1iZWRFbGVt
-ZW50OlcucUUsSFRNTEZpZWxkU2V0RWxlbWVudDpXLnFFLEhUTUxIUkVsZW1lbnQ6Vy5xRSxIVE1MSGVh
-ZEVsZW1lbnQ6Vy5xRSxIVE1MSGVhZGluZ0VsZW1lbnQ6Vy5xRSxIVE1MSHRtbEVsZW1lbnQ6Vy5xRSxI
-VE1MSUZyYW1lRWxlbWVudDpXLnFFLEhUTUxJbWFnZUVsZW1lbnQ6Vy5xRSxIVE1MSW5wdXRFbGVtZW50
-OlcucUUsSFRNTExJRWxlbWVudDpXLnFFLEhUTUxMYWJlbEVsZW1lbnQ6Vy5xRSxIVE1MTGVnZW5kRWxl
-bWVudDpXLnFFLEhUTUxMaW5rRWxlbWVudDpXLnFFLEhUTUxNYXBFbGVtZW50OlcucUUsSFRNTE1lZGlh
-RWxlbWVudDpXLnFFLEhUTUxNZW51RWxlbWVudDpXLnFFLEhUTUxNZXRhRWxlbWVudDpXLnFFLEhUTUxN
-ZXRlckVsZW1lbnQ6Vy5xRSxIVE1MTW9kRWxlbWVudDpXLnFFLEhUTUxPTGlzdEVsZW1lbnQ6Vy5xRSxI
-VE1MT2JqZWN0RWxlbWVudDpXLnFFLEhUTUxPcHRHcm91cEVsZW1lbnQ6Vy5xRSxIVE1MT3B0aW9uRWxl
-bWVudDpXLnFFLEhUTUxPdXRwdXRFbGVtZW50OlcucUUsSFRNTFBhcmFtRWxlbWVudDpXLnFFLEhUTUxQ
-aWN0dXJlRWxlbWVudDpXLnFFLEhUTUxQcmVFbGVtZW50OlcucUUsSFRNTFByb2dyZXNzRWxlbWVudDpX
-LnFFLEhUTUxRdW90ZUVsZW1lbnQ6Vy5xRSxIVE1MU2NyaXB0RWxlbWVudDpXLnFFLEhUTUxTaGFkb3dF
-bGVtZW50OlcucUUsSFRNTFNsb3RFbGVtZW50OlcucUUsSFRNTFNvdXJjZUVsZW1lbnQ6Vy5xRSxIVE1M
-U3BhbkVsZW1lbnQ6Vy5xRSxIVE1MU3R5bGVFbGVtZW50OlcucUUsSFRNTFRhYmxlQ2FwdGlvbkVsZW1l
-bnQ6Vy5xRSxIVE1MVGFibGVDZWxsRWxlbWVudDpXLnFFLEhUTUxUYWJsZURhdGFDZWxsRWxlbWVudDpX
-LnFFLEhUTUxUYWJsZUhlYWRlckNlbGxFbGVtZW50OlcucUUsSFRNTFRhYmxlQ29sRWxlbWVudDpXLnFF
-LEhUTUxUZXh0QXJlYUVsZW1lbnQ6Vy5xRSxIVE1MVGltZUVsZW1lbnQ6Vy5xRSxIVE1MVGl0bGVFbGVt
-ZW50OlcucUUsSFRNTFRyYWNrRWxlbWVudDpXLnFFLEhUTUxVTGlzdEVsZW1lbnQ6Vy5xRSxIVE1MVW5r
-bm93bkVsZW1lbnQ6Vy5xRSxIVE1MVmlkZW9FbGVtZW50OlcucUUsSFRNTERpcmVjdG9yeUVsZW1lbnQ6
-Vy5xRSxIVE1MRm9udEVsZW1lbnQ6Vy5xRSxIVE1MRnJhbWVFbGVtZW50OlcucUUsSFRNTEZyYW1lU2V0
-RWxlbWVudDpXLnFFLEhUTUxNYXJxdWVlRWxlbWVudDpXLnFFLEhUTUxFbGVtZW50OlcucUUsSFRNTEFu
-Y2hvckVsZW1lbnQ6Vy5HaCxIVE1MQXJlYUVsZW1lbnQ6Vy5mWSxIVE1MQmFzZUVsZW1lbnQ6Vy5yWixC
-bG9iOlcuQXosSFRNTEJvZHlFbGVtZW50OlcuUVAsQ0RBVEFTZWN0aW9uOlcubngsQ2hhcmFjdGVyRGF0
-YTpXLm54LENvbW1lbnQ6Vy5ueCxQcm9jZXNzaW5nSW5zdHJ1Y3Rpb246Vy5ueCxUZXh0OlcubngsQ1NT
-U3R5bGVEZWNsYXJhdGlvbjpXLm9KLE1TU3R5bGVDU1NQcm9wZXJ0aWVzOlcub0osQ1NTMlByb3BlcnRp
-ZXM6Vy5vSixYTUxEb2N1bWVudDpXLlFGLERvY3VtZW50OlcuUUYsRE9NRXhjZXB0aW9uOlcuTmgsRE9N
-SW1wbGVtZW50YXRpb246Vy5hZSxET01SZWN0UmVhZE9ubHk6Vy5JQixET01Ub2tlbkxpc3Q6Vy5OUSxF
-bGVtZW50OlcuY3YsQWJvcnRQYXltZW50RXZlbnQ6Vy5lYSxBbmltYXRpb25FdmVudDpXLmVhLEFuaW1h
-dGlvblBsYXliYWNrRXZlbnQ6Vy5lYSxBcHBsaWNhdGlvbkNhY2hlRXJyb3JFdmVudDpXLmVhLEJhY2tn
-cm91bmRGZXRjaENsaWNrRXZlbnQ6Vy5lYSxCYWNrZ3JvdW5kRmV0Y2hFdmVudDpXLmVhLEJhY2tncm91
-bmRGZXRjaEZhaWxFdmVudDpXLmVhLEJhY2tncm91bmRGZXRjaGVkRXZlbnQ6Vy5lYSxCZWZvcmVJbnN0
-YWxsUHJvbXB0RXZlbnQ6Vy5lYSxCZWZvcmVVbmxvYWRFdmVudDpXLmVhLEJsb2JFdmVudDpXLmVhLENh
-bk1ha2VQYXltZW50RXZlbnQ6Vy5lYSxDbGlwYm9hcmRFdmVudDpXLmVhLENsb3NlRXZlbnQ6Vy5lYSxD
-dXN0b21FdmVudDpXLmVhLERldmljZU1vdGlvbkV2ZW50OlcuZWEsRGV2aWNlT3JpZW50YXRpb25FdmVu
-dDpXLmVhLEVycm9yRXZlbnQ6Vy5lYSxFeHRlbmRhYmxlRXZlbnQ6Vy5lYSxFeHRlbmRhYmxlTWVzc2Fn
-ZUV2ZW50OlcuZWEsRmV0Y2hFdmVudDpXLmVhLEZvbnRGYWNlU2V0TG9hZEV2ZW50OlcuZWEsRm9yZWln
-bkZldGNoRXZlbnQ6Vy5lYSxHYW1lcGFkRXZlbnQ6Vy5lYSxIYXNoQ2hhbmdlRXZlbnQ6Vy5lYSxJbnN0
-YWxsRXZlbnQ6Vy5lYSxNZWRpYUVuY3J5cHRlZEV2ZW50OlcuZWEsTWVkaWFLZXlNZXNzYWdlRXZlbnQ6
-Vy5lYSxNZWRpYVF1ZXJ5TGlzdEV2ZW50OlcuZWEsTWVkaWFTdHJlYW1FdmVudDpXLmVhLE1lZGlhU3Ry
-ZWFtVHJhY2tFdmVudDpXLmVhLE1lc3NhZ2VFdmVudDpXLmVhLE1JRElDb25uZWN0aW9uRXZlbnQ6Vy5l
-YSxNSURJTWVzc2FnZUV2ZW50OlcuZWEsTXV0YXRpb25FdmVudDpXLmVhLE5vdGlmaWNhdGlvbkV2ZW50
-OlcuZWEsUGFnZVRyYW5zaXRpb25FdmVudDpXLmVhLFBheW1lbnRSZXF1ZXN0RXZlbnQ6Vy5lYSxQYXlt
-ZW50UmVxdWVzdFVwZGF0ZUV2ZW50OlcuZWEsUG9wU3RhdGVFdmVudDpXLmVhLFByZXNlbnRhdGlvbkNv
-bm5lY3Rpb25BdmFpbGFibGVFdmVudDpXLmVhLFByZXNlbnRhdGlvbkNvbm5lY3Rpb25DbG9zZUV2ZW50
-OlcuZWEsUHJvbWlzZVJlamVjdGlvbkV2ZW50OlcuZWEsUHVzaEV2ZW50OlcuZWEsUlRDRGF0YUNoYW5u
-ZWxFdmVudDpXLmVhLFJUQ0RUTUZUb25lQ2hhbmdlRXZlbnQ6Vy5lYSxSVENQZWVyQ29ubmVjdGlvbklj
-ZUV2ZW50OlcuZWEsUlRDVHJhY2tFdmVudDpXLmVhLFNlY3VyaXR5UG9saWN5VmlvbGF0aW9uRXZlbnQ6
-Vy5lYSxTZW5zb3JFcnJvckV2ZW50OlcuZWEsU3BlZWNoUmVjb2duaXRpb25FcnJvcjpXLmVhLFNwZWVj
-aFJlY29nbml0aW9uRXZlbnQ6Vy5lYSxTcGVlY2hTeW50aGVzaXNFdmVudDpXLmVhLFN0b3JhZ2VFdmVu
-dDpXLmVhLFN5bmNFdmVudDpXLmVhLFRyYWNrRXZlbnQ6Vy5lYSxUcmFuc2l0aW9uRXZlbnQ6Vy5lYSxX
-ZWJLaXRUcmFuc2l0aW9uRXZlbnQ6Vy5lYSxWUkRldmljZUV2ZW50OlcuZWEsVlJEaXNwbGF5RXZlbnQ6
-Vy5lYSxWUlNlc3Npb25FdmVudDpXLmVhLE1vam9JbnRlcmZhY2VSZXF1ZXN0RXZlbnQ6Vy5lYSxVU0JD
-b25uZWN0aW9uRXZlbnQ6Vy5lYSxJREJWZXJzaW9uQ2hhbmdlRXZlbnQ6Vy5lYSxBdWRpb1Byb2Nlc3Np
-bmdFdmVudDpXLmVhLE9mZmxpbmVBdWRpb0NvbXBsZXRpb25FdmVudDpXLmVhLFdlYkdMQ29udGV4dEV2
-ZW50OlcuZWEsRXZlbnQ6Vy5lYSxJbnB1dEV2ZW50OlcuZWEsU3VibWl0RXZlbnQ6Vy5lYSxFdmVudFRh
-cmdldDpXLkQwLEZpbGU6Vy5oSCxIVE1MRm9ybUVsZW1lbnQ6Vy5oNCxIaXN0b3J5OlcuYnIsSFRNTERv
-Y3VtZW50OlcuVmIsWE1MSHR0cFJlcXVlc3Q6Vy5mSixYTUxIdHRwUmVxdWVzdEV2ZW50VGFyZ2V0Olcu
-d2EsSW1hZ2VEYXRhOlcuU2csTG9jYXRpb246Vy53NyxNb3VzZUV2ZW50OlcuQWosRHJhZ0V2ZW50Olcu
-QWosUG9pbnRlckV2ZW50OlcuQWosV2hlZWxFdmVudDpXLkFqLERvY3VtZW50RnJhZ21lbnQ6Vy51SCxT
-aGFkb3dSb290OlcudUgsRG9jdW1lbnRUeXBlOlcudUgsTm9kZTpXLnVILE5vZGVMaXN0OlcuQkgsUmFk
-aW9Ob2RlTGlzdDpXLkJILEhUTUxQYXJhZ3JhcGhFbGVtZW50OlcuU04sUHJvZ3Jlc3NFdmVudDpXLmV3
-LFJlc291cmNlUHJvZ3Jlc3NFdmVudDpXLmV3LEhUTUxTZWxlY3RFbGVtZW50OlcubHAsSFRNTFRhYmxl
-RWxlbWVudDpXLlRiLEhUTUxUYWJsZVJvd0VsZW1lbnQ6Vy5JdixIVE1MVGFibGVTZWN0aW9uRWxlbWVu
-dDpXLldQLEhUTUxUZW1wbGF0ZUVsZW1lbnQ6Vy55WSxDb21wb3NpdGlvbkV2ZW50OlcudzYsRm9jdXNF
-dmVudDpXLnc2LEtleWJvYXJkRXZlbnQ6Vy53NixUZXh0RXZlbnQ6Vy53NixUb3VjaEV2ZW50OlcudzYs
-VUlFdmVudDpXLnc2LFdpbmRvdzpXLks1LERPTVdpbmRvdzpXLks1LERlZGljYXRlZFdvcmtlckdsb2Jh
-bFNjb3BlOlcuQ20sU2VydmljZVdvcmtlckdsb2JhbFNjb3BlOlcuQ20sU2hhcmVkV29ya2VyR2xvYmFs
-U2NvcGU6Vy5DbSxXb3JrZXJHbG9iYWxTY29wZTpXLkNtLEF0dHI6Vy5DUSxDbGllbnRSZWN0OlcudzQs
-RE9NUmVjdDpXLnc0LE5hbWVkTm9kZU1hcDpXLnJoLE1vek5hbWVkQXR0ck1hcDpXLnJoLElEQktleVJh
-bmdlOlAuaEYsU1ZHU2NyaXB0RWxlbWVudDpQLm5kLFNWR0FFbGVtZW50OlAuaGksU1ZHQW5pbWF0ZUVs
-ZW1lbnQ6UC5oaSxTVkdBbmltYXRlTW90aW9uRWxlbWVudDpQLmhpLFNWR0FuaW1hdGVUcmFuc2Zvcm1F
-bGVtZW50OlAuaGksU1ZHQW5pbWF0aW9uRWxlbWVudDpQLmhpLFNWR0NpcmNsZUVsZW1lbnQ6UC5oaSxT
-VkdDbGlwUGF0aEVsZW1lbnQ6UC5oaSxTVkdEZWZzRWxlbWVudDpQLmhpLFNWR0Rlc2NFbGVtZW50OlAu
-aGksU1ZHRGlzY2FyZEVsZW1lbnQ6UC5oaSxTVkdFbGxpcHNlRWxlbWVudDpQLmhpLFNWR0ZFQmxlbmRF
-bGVtZW50OlAuaGksU1ZHRkVDb2xvck1hdHJpeEVsZW1lbnQ6UC5oaSxTVkdGRUNvbXBvbmVudFRyYW5z
-ZmVyRWxlbWVudDpQLmhpLFNWR0ZFQ29tcG9zaXRlRWxlbWVudDpQLmhpLFNWR0ZFQ29udm9sdmVNYXRy
-aXhFbGVtZW50OlAuaGksU1ZHRkVEaWZmdXNlTGlnaHRpbmdFbGVtZW50OlAuaGksU1ZHRkVEaXNwbGFj
-ZW1lbnRNYXBFbGVtZW50OlAuaGksU1ZHRkVEaXN0YW50TGlnaHRFbGVtZW50OlAuaGksU1ZHRkVGbG9v
-ZEVsZW1lbnQ6UC5oaSxTVkdGRUZ1bmNBRWxlbWVudDpQLmhpLFNWR0ZFRnVuY0JFbGVtZW50OlAuaGks
-U1ZHRkVGdW5jR0VsZW1lbnQ6UC5oaSxTVkdGRUZ1bmNSRWxlbWVudDpQLmhpLFNWR0ZFR2F1c3NpYW5C
-bHVyRWxlbWVudDpQLmhpLFNWR0ZFSW1hZ2VFbGVtZW50OlAuaGksU1ZHRkVNZXJnZUVsZW1lbnQ6UC5o
-aSxTVkdGRU1lcmdlTm9kZUVsZW1lbnQ6UC5oaSxTVkdGRU1vcnBob2xvZ3lFbGVtZW50OlAuaGksU1ZH
-RkVPZmZzZXRFbGVtZW50OlAuaGksU1ZHRkVQb2ludExpZ2h0RWxlbWVudDpQLmhpLFNWR0ZFU3BlY3Vs
-YXJMaWdodGluZ0VsZW1lbnQ6UC5oaSxTVkdGRVNwb3RMaWdodEVsZW1lbnQ6UC5oaSxTVkdGRVRpbGVF
-bGVtZW50OlAuaGksU1ZHRkVUdXJidWxlbmNlRWxlbWVudDpQLmhpLFNWR0ZpbHRlckVsZW1lbnQ6UC5o
-aSxTVkdGb3JlaWduT2JqZWN0RWxlbWVudDpQLmhpLFNWR0dFbGVtZW50OlAuaGksU1ZHR2VvbWV0cnlF
-bGVtZW50OlAuaGksU1ZHR3JhcGhpY3NFbGVtZW50OlAuaGksU1ZHSW1hZ2VFbGVtZW50OlAuaGksU1ZH
-TGluZUVsZW1lbnQ6UC5oaSxTVkdMaW5lYXJHcmFkaWVudEVsZW1lbnQ6UC5oaSxTVkdNYXJrZXJFbGVt
-ZW50OlAuaGksU1ZHTWFza0VsZW1lbnQ6UC5oaSxTVkdNZXRhZGF0YUVsZW1lbnQ6UC5oaSxTVkdQYXRo
-RWxlbWVudDpQLmhpLFNWR1BhdHRlcm5FbGVtZW50OlAuaGksU1ZHUG9seWdvbkVsZW1lbnQ6UC5oaSxT
-VkdQb2x5bGluZUVsZW1lbnQ6UC5oaSxTVkdSYWRpYWxHcmFkaWVudEVsZW1lbnQ6UC5oaSxTVkdSZWN0
-RWxlbWVudDpQLmhpLFNWR1NldEVsZW1lbnQ6UC5oaSxTVkdTdG9wRWxlbWVudDpQLmhpLFNWR1N0eWxl
-RWxlbWVudDpQLmhpLFNWR1NWR0VsZW1lbnQ6UC5oaSxTVkdTd2l0Y2hFbGVtZW50OlAuaGksU1ZHU3lt
-Ym9sRWxlbWVudDpQLmhpLFNWR1RTcGFuRWxlbWVudDpQLmhpLFNWR1RleHRDb250ZW50RWxlbWVudDpQ
-LmhpLFNWR1RleHRFbGVtZW50OlAuaGksU1ZHVGV4dFBhdGhFbGVtZW50OlAuaGksU1ZHVGV4dFBvc2l0
-aW9uaW5nRWxlbWVudDpQLmhpLFNWR1RpdGxlRWxlbWVudDpQLmhpLFNWR1VzZUVsZW1lbnQ6UC5oaSxT
-VkdWaWV3RWxlbWVudDpQLmhpLFNWR0dyYWRpZW50RWxlbWVudDpQLmhpLFNWR0NvbXBvbmVudFRyYW5z
-ZmVyRnVuY3Rpb25FbGVtZW50OlAuaGksU1ZHRkVEcm9wU2hhZG93RWxlbWVudDpQLmhpLFNWR01QYXRo
-RWxlbWVudDpQLmhpLFNWR0VsZW1lbnQ6UC5oaX0pCmh1bmtIZWxwZXJzLnNldE9yVXBkYXRlTGVhZlRh
-Z3Moe0RPTUVycm9yOnRydWUsTWVkaWFFcnJvcjp0cnVlLE5hdmlnYXRvcjp0cnVlLE5hdmlnYXRvckNv
-bmN1cnJlbnRIYXJkd2FyZTp0cnVlLE5hdmlnYXRvclVzZXJNZWRpYUVycm9yOnRydWUsT3ZlcmNvbnN0
-cmFpbmVkRXJyb3I6dHJ1ZSxQb3NpdGlvbkVycm9yOnRydWUsR2VvbG9jYXRpb25Qb3NpdGlvbkVycm9y
-OnRydWUsUmFuZ2U6dHJ1ZSxTUUxFcnJvcjp0cnVlLERhdGFWaWV3OnRydWUsQXJyYXlCdWZmZXJWaWV3
-OmZhbHNlLEZsb2F0MzJBcnJheTp0cnVlLEZsb2F0NjRBcnJheTp0cnVlLEludDE2QXJyYXk6dHJ1ZSxJ
-bnQzMkFycmF5OnRydWUsSW50OEFycmF5OnRydWUsVWludDE2QXJyYXk6dHJ1ZSxVaW50MzJBcnJheTp0
-cnVlLFVpbnQ4Q2xhbXBlZEFycmF5OnRydWUsQ2FudmFzUGl4ZWxBcnJheTp0cnVlLFVpbnQ4QXJyYXk6
-ZmFsc2UsSFRNTEF1ZGlvRWxlbWVudDp0cnVlLEhUTUxCUkVsZW1lbnQ6dHJ1ZSxIVE1MQnV0dG9uRWxl
-bWVudDp0cnVlLEhUTUxDYW52YXNFbGVtZW50OnRydWUsSFRNTENvbnRlbnRFbGVtZW50OnRydWUsSFRN
-TERMaXN0RWxlbWVudDp0cnVlLEhUTUxEYXRhRWxlbWVudDp0cnVlLEhUTUxEYXRhTGlzdEVsZW1lbnQ6
-dHJ1ZSxIVE1MRGV0YWlsc0VsZW1lbnQ6dHJ1ZSxIVE1MRGlhbG9nRWxlbWVudDp0cnVlLEhUTUxEaXZF
-bGVtZW50OnRydWUsSFRNTEVtYmVkRWxlbWVudDp0cnVlLEhUTUxGaWVsZFNldEVsZW1lbnQ6dHJ1ZSxI
-VE1MSFJFbGVtZW50OnRydWUsSFRNTEhlYWRFbGVtZW50OnRydWUsSFRNTEhlYWRpbmdFbGVtZW50OnRy
-dWUsSFRNTEh0bWxFbGVtZW50OnRydWUsSFRNTElGcmFtZUVsZW1lbnQ6dHJ1ZSxIVE1MSW1hZ2VFbGVt
-ZW50OnRydWUsSFRNTElucHV0RWxlbWVudDp0cnVlLEhUTUxMSUVsZW1lbnQ6dHJ1ZSxIVE1MTGFiZWxF
-bGVtZW50OnRydWUsSFRNTExlZ2VuZEVsZW1lbnQ6dHJ1ZSxIVE1MTGlua0VsZW1lbnQ6dHJ1ZSxIVE1M
-TWFwRWxlbWVudDp0cnVlLEhUTUxNZWRpYUVsZW1lbnQ6dHJ1ZSxIVE1MTWVudUVsZW1lbnQ6dHJ1ZSxI
-VE1MTWV0YUVsZW1lbnQ6dHJ1ZSxIVE1MTWV0ZXJFbGVtZW50OnRydWUsSFRNTE1vZEVsZW1lbnQ6dHJ1
-ZSxIVE1MT0xpc3RFbGVtZW50OnRydWUsSFRNTE9iamVjdEVsZW1lbnQ6dHJ1ZSxIVE1MT3B0R3JvdXBF
-bGVtZW50OnRydWUsSFRNTE9wdGlvbkVsZW1lbnQ6dHJ1ZSxIVE1MT3V0cHV0RWxlbWVudDp0cnVlLEhU
-TUxQYXJhbUVsZW1lbnQ6dHJ1ZSxIVE1MUGljdHVyZUVsZW1lbnQ6dHJ1ZSxIVE1MUHJlRWxlbWVudDp0
-cnVlLEhUTUxQcm9ncmVzc0VsZW1lbnQ6dHJ1ZSxIVE1MUXVvdGVFbGVtZW50OnRydWUsSFRNTFNjcmlw
-dEVsZW1lbnQ6dHJ1ZSxIVE1MU2hhZG93RWxlbWVudDp0cnVlLEhUTUxTbG90RWxlbWVudDp0cnVlLEhU
-TUxTb3VyY2VFbGVtZW50OnRydWUsSFRNTFNwYW5FbGVtZW50OnRydWUsSFRNTFN0eWxlRWxlbWVudDp0
-cnVlLEhUTUxUYWJsZUNhcHRpb25FbGVtZW50OnRydWUsSFRNTFRhYmxlQ2VsbEVsZW1lbnQ6dHJ1ZSxI
-VE1MVGFibGVEYXRhQ2VsbEVsZW1lbnQ6dHJ1ZSxIVE1MVGFibGVIZWFkZXJDZWxsRWxlbWVudDp0cnVl
-LEhUTUxUYWJsZUNvbEVsZW1lbnQ6dHJ1ZSxIVE1MVGV4dEFyZWFFbGVtZW50OnRydWUsSFRNTFRpbWVF
-bGVtZW50OnRydWUsSFRNTFRpdGxlRWxlbWVudDp0cnVlLEhUTUxUcmFja0VsZW1lbnQ6dHJ1ZSxIVE1M
-VUxpc3RFbGVtZW50OnRydWUsSFRNTFVua25vd25FbGVtZW50OnRydWUsSFRNTFZpZGVvRWxlbWVudDp0
-cnVlLEhUTUxEaXJlY3RvcnlFbGVtZW50OnRydWUsSFRNTEZvbnRFbGVtZW50OnRydWUsSFRNTEZyYW1l
-RWxlbWVudDp0cnVlLEhUTUxGcmFtZVNldEVsZW1lbnQ6dHJ1ZSxIVE1MTWFycXVlZUVsZW1lbnQ6dHJ1
-ZSxIVE1MRWxlbWVudDpmYWxzZSxIVE1MQW5jaG9yRWxlbWVudDp0cnVlLEhUTUxBcmVhRWxlbWVudDp0
-cnVlLEhUTUxCYXNlRWxlbWVudDp0cnVlLEJsb2I6ZmFsc2UsSFRNTEJvZHlFbGVtZW50OnRydWUsQ0RB
-VEFTZWN0aW9uOnRydWUsQ2hhcmFjdGVyRGF0YTp0cnVlLENvbW1lbnQ6dHJ1ZSxQcm9jZXNzaW5nSW5z
-dHJ1Y3Rpb246dHJ1ZSxUZXh0OnRydWUsQ1NTU3R5bGVEZWNsYXJhdGlvbjp0cnVlLE1TU3R5bGVDU1NQ
-cm9wZXJ0aWVzOnRydWUsQ1NTMlByb3BlcnRpZXM6dHJ1ZSxYTUxEb2N1bWVudDp0cnVlLERvY3VtZW50
-OmZhbHNlLERPTUV4Y2VwdGlvbjp0cnVlLERPTUltcGxlbWVudGF0aW9uOnRydWUsRE9NUmVjdFJlYWRP
-bmx5OmZhbHNlLERPTVRva2VuTGlzdDp0cnVlLEVsZW1lbnQ6ZmFsc2UsQWJvcnRQYXltZW50RXZlbnQ6
-dHJ1ZSxBbmltYXRpb25FdmVudDp0cnVlLEFuaW1hdGlvblBsYXliYWNrRXZlbnQ6dHJ1ZSxBcHBsaWNh
-dGlvbkNhY2hlRXJyb3JFdmVudDp0cnVlLEJhY2tncm91bmRGZXRjaENsaWNrRXZlbnQ6dHJ1ZSxCYWNr
-Z3JvdW5kRmV0Y2hFdmVudDp0cnVlLEJhY2tncm91bmRGZXRjaEZhaWxFdmVudDp0cnVlLEJhY2tncm91
-bmRGZXRjaGVkRXZlbnQ6dHJ1ZSxCZWZvcmVJbnN0YWxsUHJvbXB0RXZlbnQ6dHJ1ZSxCZWZvcmVVbmxv
-YWRFdmVudDp0cnVlLEJsb2JFdmVudDp0cnVlLENhbk1ha2VQYXltZW50RXZlbnQ6dHJ1ZSxDbGlwYm9h
-cmRFdmVudDp0cnVlLENsb3NlRXZlbnQ6dHJ1ZSxDdXN0b21FdmVudDp0cnVlLERldmljZU1vdGlvbkV2
-ZW50OnRydWUsRGV2aWNlT3JpZW50YXRpb25FdmVudDp0cnVlLEVycm9yRXZlbnQ6dHJ1ZSxFeHRlbmRh
-YmxlRXZlbnQ6dHJ1ZSxFeHRlbmRhYmxlTWVzc2FnZUV2ZW50OnRydWUsRmV0Y2hFdmVudDp0cnVlLEZv
-bnRGYWNlU2V0TG9hZEV2ZW50OnRydWUsRm9yZWlnbkZldGNoRXZlbnQ6dHJ1ZSxHYW1lcGFkRXZlbnQ6
-dHJ1ZSxIYXNoQ2hhbmdlRXZlbnQ6dHJ1ZSxJbnN0YWxsRXZlbnQ6dHJ1ZSxNZWRpYUVuY3J5cHRlZEV2
-ZW50OnRydWUsTWVkaWFLZXlNZXNzYWdlRXZlbnQ6dHJ1ZSxNZWRpYVF1ZXJ5TGlzdEV2ZW50OnRydWUs
-TWVkaWFTdHJlYW1FdmVudDp0cnVlLE1lZGlhU3RyZWFtVHJhY2tFdmVudDp0cnVlLE1lc3NhZ2VFdmVu
-dDp0cnVlLE1JRElDb25uZWN0aW9uRXZlbnQ6dHJ1ZSxNSURJTWVzc2FnZUV2ZW50OnRydWUsTXV0YXRp
-b25FdmVudDp0cnVlLE5vdGlmaWNhdGlvbkV2ZW50OnRydWUsUGFnZVRyYW5zaXRpb25FdmVudDp0cnVl
-LFBheW1lbnRSZXF1ZXN0RXZlbnQ6dHJ1ZSxQYXltZW50UmVxdWVzdFVwZGF0ZUV2ZW50OnRydWUsUG9w
-U3RhdGVFdmVudDp0cnVlLFByZXNlbnRhdGlvbkNvbm5lY3Rpb25BdmFpbGFibGVFdmVudDp0cnVlLFBy
-ZXNlbnRhdGlvbkNvbm5lY3Rpb25DbG9zZUV2ZW50OnRydWUsUHJvbWlzZVJlamVjdGlvbkV2ZW50OnRy
-dWUsUHVzaEV2ZW50OnRydWUsUlRDRGF0YUNoYW5uZWxFdmVudDp0cnVlLFJUQ0RUTUZUb25lQ2hhbmdl
-RXZlbnQ6dHJ1ZSxSVENQZWVyQ29ubmVjdGlvbkljZUV2ZW50OnRydWUsUlRDVHJhY2tFdmVudDp0cnVl
-LFNlY3VyaXR5UG9saWN5VmlvbGF0aW9uRXZlbnQ6dHJ1ZSxTZW5zb3JFcnJvckV2ZW50OnRydWUsU3Bl
-ZWNoUmVjb2duaXRpb25FcnJvcjp0cnVlLFNwZWVjaFJlY29nbml0aW9uRXZlbnQ6dHJ1ZSxTcGVlY2hT
-eW50aGVzaXNFdmVudDp0cnVlLFN0b3JhZ2VFdmVudDp0cnVlLFN5bmNFdmVudDp0cnVlLFRyYWNrRXZl
-bnQ6dHJ1ZSxUcmFuc2l0aW9uRXZlbnQ6dHJ1ZSxXZWJLaXRUcmFuc2l0aW9uRXZlbnQ6dHJ1ZSxWUkRl
-dmljZUV2ZW50OnRydWUsVlJEaXNwbGF5RXZlbnQ6dHJ1ZSxWUlNlc3Npb25FdmVudDp0cnVlLE1vam9J
-bnRlcmZhY2VSZXF1ZXN0RXZlbnQ6dHJ1ZSxVU0JDb25uZWN0aW9uRXZlbnQ6dHJ1ZSxJREJWZXJzaW9u
-Q2hhbmdlRXZlbnQ6dHJ1ZSxBdWRpb1Byb2Nlc3NpbmdFdmVudDp0cnVlLE9mZmxpbmVBdWRpb0NvbXBs
-ZXRpb25FdmVudDp0cnVlLFdlYkdMQ29udGV4dEV2ZW50OnRydWUsRXZlbnQ6ZmFsc2UsSW5wdXRFdmVu
-dDpmYWxzZSxTdWJtaXRFdmVudDpmYWxzZSxFdmVudFRhcmdldDpmYWxzZSxGaWxlOnRydWUsSFRNTEZv
-cm1FbGVtZW50OnRydWUsSGlzdG9yeTp0cnVlLEhUTUxEb2N1bWVudDp0cnVlLFhNTEh0dHBSZXF1ZXN0
-OnRydWUsWE1MSHR0cFJlcXVlc3RFdmVudFRhcmdldDpmYWxzZSxJbWFnZURhdGE6dHJ1ZSxMb2NhdGlv
-bjp0cnVlLE1vdXNlRXZlbnQ6dHJ1ZSxEcmFnRXZlbnQ6dHJ1ZSxQb2ludGVyRXZlbnQ6dHJ1ZSxXaGVl
-bEV2ZW50OnRydWUsRG9jdW1lbnRGcmFnbWVudDp0cnVlLFNoYWRvd1Jvb3Q6dHJ1ZSxEb2N1bWVudFR5
-cGU6dHJ1ZSxOb2RlOmZhbHNlLE5vZGVMaXN0OnRydWUsUmFkaW9Ob2RlTGlzdDp0cnVlLEhUTUxQYXJh
-Z3JhcGhFbGVtZW50OnRydWUsUHJvZ3Jlc3NFdmVudDp0cnVlLFJlc291cmNlUHJvZ3Jlc3NFdmVudDp0
-cnVlLEhUTUxTZWxlY3RFbGVtZW50OnRydWUsSFRNTFRhYmxlRWxlbWVudDp0cnVlLEhUTUxUYWJsZVJv
-d0VsZW1lbnQ6dHJ1ZSxIVE1MVGFibGVTZWN0aW9uRWxlbWVudDp0cnVlLEhUTUxUZW1wbGF0ZUVsZW1l
-bnQ6dHJ1ZSxDb21wb3NpdGlvbkV2ZW50OnRydWUsRm9jdXNFdmVudDp0cnVlLEtleWJvYXJkRXZlbnQ6
-dHJ1ZSxUZXh0RXZlbnQ6dHJ1ZSxUb3VjaEV2ZW50OnRydWUsVUlFdmVudDpmYWxzZSxXaW5kb3c6dHJ1
-ZSxET01XaW5kb3c6dHJ1ZSxEZWRpY2F0ZWRXb3JrZXJHbG9iYWxTY29wZTp0cnVlLFNlcnZpY2VXb3Jr
-ZXJHbG9iYWxTY29wZTp0cnVlLFNoYXJlZFdvcmtlckdsb2JhbFNjb3BlOnRydWUsV29ya2VyR2xvYmFs
-U2NvcGU6dHJ1ZSxBdHRyOnRydWUsQ2xpZW50UmVjdDp0cnVlLERPTVJlY3Q6dHJ1ZSxOYW1lZE5vZGVN
-YXA6dHJ1ZSxNb3pOYW1lZEF0dHJNYXA6dHJ1ZSxJREJLZXlSYW5nZTp0cnVlLFNWR1NjcmlwdEVsZW1l
-bnQ6dHJ1ZSxTVkdBRWxlbWVudDp0cnVlLFNWR0FuaW1hdGVFbGVtZW50OnRydWUsU1ZHQW5pbWF0ZU1v
-dGlvbkVsZW1lbnQ6dHJ1ZSxTVkdBbmltYXRlVHJhbnNmb3JtRWxlbWVudDp0cnVlLFNWR0FuaW1hdGlv
-bkVsZW1lbnQ6dHJ1ZSxTVkdDaXJjbGVFbGVtZW50OnRydWUsU1ZHQ2xpcFBhdGhFbGVtZW50OnRydWUs
-U1ZHRGVmc0VsZW1lbnQ6dHJ1ZSxTVkdEZXNjRWxlbWVudDp0cnVlLFNWR0Rpc2NhcmRFbGVtZW50OnRy
-dWUsU1ZHRWxsaXBzZUVsZW1lbnQ6dHJ1ZSxTVkdGRUJsZW5kRWxlbWVudDp0cnVlLFNWR0ZFQ29sb3JN
-YXRyaXhFbGVtZW50OnRydWUsU1ZHRkVDb21wb25lbnRUcmFuc2ZlckVsZW1lbnQ6dHJ1ZSxTVkdGRUNv
-bXBvc2l0ZUVsZW1lbnQ6dHJ1ZSxTVkdGRUNvbnZvbHZlTWF0cml4RWxlbWVudDp0cnVlLFNWR0ZFRGlm
-ZnVzZUxpZ2h0aW5nRWxlbWVudDp0cnVlLFNWR0ZFRGlzcGxhY2VtZW50TWFwRWxlbWVudDp0cnVlLFNW
-R0ZFRGlzdGFudExpZ2h0RWxlbWVudDp0cnVlLFNWR0ZFRmxvb2RFbGVtZW50OnRydWUsU1ZHRkVGdW5j
-QUVsZW1lbnQ6dHJ1ZSxTVkdGRUZ1bmNCRWxlbWVudDp0cnVlLFNWR0ZFRnVuY0dFbGVtZW50OnRydWUs
-U1ZHRkVGdW5jUkVsZW1lbnQ6dHJ1ZSxTVkdGRUdhdXNzaWFuQmx1ckVsZW1lbnQ6dHJ1ZSxTVkdGRUlt
-YWdlRWxlbWVudDp0cnVlLFNWR0ZFTWVyZ2VFbGVtZW50OnRydWUsU1ZHRkVNZXJnZU5vZGVFbGVtZW50
-OnRydWUsU1ZHRkVNb3JwaG9sb2d5RWxlbWVudDp0cnVlLFNWR0ZFT2Zmc2V0RWxlbWVudDp0cnVlLFNW
-R0ZFUG9pbnRMaWdodEVsZW1lbnQ6dHJ1ZSxTVkdGRVNwZWN1bGFyTGlnaHRpbmdFbGVtZW50OnRydWUs
-U1ZHRkVTcG90TGlnaHRFbGVtZW50OnRydWUsU1ZHRkVUaWxlRWxlbWVudDp0cnVlLFNWR0ZFVHVyYnVs
-ZW5jZUVsZW1lbnQ6dHJ1ZSxTVkdGaWx0ZXJFbGVtZW50OnRydWUsU1ZHRm9yZWlnbk9iamVjdEVsZW1l
-bnQ6dHJ1ZSxTVkdHRWxlbWVudDp0cnVlLFNWR0dlb21ldHJ5RWxlbWVudDp0cnVlLFNWR0dyYXBoaWNz
-RWxlbWVudDp0cnVlLFNWR0ltYWdlRWxlbWVudDp0cnVlLFNWR0xpbmVFbGVtZW50OnRydWUsU1ZHTGlu
-ZWFyR3JhZGllbnRFbGVtZW50OnRydWUsU1ZHTWFya2VyRWxlbWVudDp0cnVlLFNWR01hc2tFbGVtZW50
-OnRydWUsU1ZHTWV0YWRhdGFFbGVtZW50OnRydWUsU1ZHUGF0aEVsZW1lbnQ6dHJ1ZSxTVkdQYXR0ZXJu
-RWxlbWVudDp0cnVlLFNWR1BvbHlnb25FbGVtZW50OnRydWUsU1ZHUG9seWxpbmVFbGVtZW50OnRydWUs
-U1ZHUmFkaWFsR3JhZGllbnRFbGVtZW50OnRydWUsU1ZHUmVjdEVsZW1lbnQ6dHJ1ZSxTVkdTZXRFbGVt
-ZW50OnRydWUsU1ZHU3RvcEVsZW1lbnQ6dHJ1ZSxTVkdTdHlsZUVsZW1lbnQ6dHJ1ZSxTVkdTVkdFbGVt
-ZW50OnRydWUsU1ZHU3dpdGNoRWxlbWVudDp0cnVlLFNWR1N5bWJvbEVsZW1lbnQ6dHJ1ZSxTVkdUU3Bh
-bkVsZW1lbnQ6dHJ1ZSxTVkdUZXh0Q29udGVudEVsZW1lbnQ6dHJ1ZSxTVkdUZXh0RWxlbWVudDp0cnVl
-LFNWR1RleHRQYXRoRWxlbWVudDp0cnVlLFNWR1RleHRQb3NpdGlvbmluZ0VsZW1lbnQ6dHJ1ZSxTVkdU
-aXRsZUVsZW1lbnQ6dHJ1ZSxTVkdVc2VFbGVtZW50OnRydWUsU1ZHVmlld0VsZW1lbnQ6dHJ1ZSxTVkdH
-cmFkaWVudEVsZW1lbnQ6dHJ1ZSxTVkdDb21wb25lbnRUcmFuc2ZlckZ1bmN0aW9uRWxlbWVudDp0cnVl
-LFNWR0ZFRHJvcFNoYWRvd0VsZW1lbnQ6dHJ1ZSxTVkdNUGF0aEVsZW1lbnQ6dHJ1ZSxTVkdFbGVtZW50
-OmZhbHNlfSkKSC5MWi4kbmF0aXZlU3VwZXJjbGFzc1RhZz0iQXJyYXlCdWZmZXJWaWV3IgpILlJHLiRu
-YXRpdmVTdXBlcmNsYXNzVGFnPSJBcnJheUJ1ZmZlclZpZXciCkguVlAuJG5hdGl2ZVN1cGVyY2xhc3NU
-YWc9IkFycmF5QnVmZmVyVmlldyIKSC5EZy4kbmF0aXZlU3VwZXJjbGFzc1RhZz0iQXJyYXlCdWZmZXJW
-aWV3IgpILldCLiRuYXRpdmVTdXBlcmNsYXNzVGFnPSJBcnJheUJ1ZmZlclZpZXciCkguWkcuJG5hdGl2
-ZVN1cGVyY2xhc3NUYWc9IkFycmF5QnVmZmVyVmlldyIKSC5QZy4kbmF0aXZlU3VwZXJjbGFzc1RhZz0i
-QXJyYXlCdWZmZXJWaWV3In0pKCkKY29udmVydEFsbFRvRmFzdE9iamVjdCh3KQpjb252ZXJ0VG9GYXN0
-T2JqZWN0KCQpOyhmdW5jdGlvbihhKXtpZih0eXBlb2YgZG9jdW1lbnQ9PT0idW5kZWZpbmVkIil7YShu
-dWxsKQpyZXR1cm59aWYodHlwZW9mIGRvY3VtZW50LmN1cnJlbnRTY3JpcHQhPSJ1bmRlZmluZWQiKXth
-KGRvY3VtZW50LmN1cnJlbnRTY3JpcHQpCnJldHVybn12YXIgcz1kb2N1bWVudC5zY3JpcHRzCmZ1bmN0
-aW9uIG9uTG9hZChiKXtmb3IodmFyIHE9MDtxPHMubGVuZ3RoOysrcSlzW3FdLnJlbW92ZUV2ZW50TGlz
-dGVuZXIoImxvYWQiLG9uTG9hZCxmYWxzZSkKYShiLnRhcmdldCl9Zm9yKHZhciByPTA7cjxzLmxlbmd0
-aDsrK3Ipc1tyXS5hZGRFdmVudExpc3RlbmVyKCJsb2FkIixvbkxvYWQsZmFsc2UpfSkoZnVuY3Rpb24o
-YSl7di5jdXJyZW50U2NyaXB0PWEKdmFyIHM9TC5JcQppZih0eXBlb2YgZGFydE1haW5SdW5uZXI9PT0i
-ZnVuY3Rpb24iKWRhcnRNYWluUnVubmVyKHMsW10pCmVsc2UgcyhbXSl9KX0pKCkKLy8jIHNvdXJjZU1h
-cHBpbmdVUkw9bWlncmF0aW9uLmpzLm1hcAo=
+IHRoaXMuRFcoYSxiLGMsZCkKcz1kb2N1bWVudApyPXMuY3JlYXRlRG9jdW1lbnRGcmFnbWVudCgpCnM9
+bmV3IFcuZTcoQy5JZS5yNihzLmNyZWF0ZUVsZW1lbnQoInRhYmxlIiksYixjLGQpKQpzPW5ldyBXLmU3
+KHMuZ3I4KHMpKQpuZXcgVy5lNyhyKS5GVigwLG5ldyBXLmU3KHMuZ3I4KHMpKSkKcmV0dXJuIHJ9fQpX
+LldQLnByb3RvdHlwZT17CnI2OmZ1bmN0aW9uKGEsYixjLGQpe3ZhciBzLHIKaWYoImNyZWF0ZUNvbnRl
+eHR1YWxGcmFnbWVudCIgaW4gd2luZG93LlJhbmdlLnByb3RvdHlwZSlyZXR1cm4gdGhpcy5EVyhhLGIs
+YyxkKQpzPWRvY3VtZW50CnI9cy5jcmVhdGVEb2N1bWVudEZyYWdtZW50KCkKcz1uZXcgVy5lNyhDLkll
+LnI2KHMuY3JlYXRlRWxlbWVudCgidGFibGUiKSxiLGMsZCkpCm5ldyBXLmU3KHIpLkZWKDAsbmV3IFcu
+ZTcocy5ncjgocykpKQpyZXR1cm4gcn19ClcueVkucHJvdG90eXBlPXsKcGs6ZnVuY3Rpb24oYSxiLGMp
+e3ZhciBzLHIKdGhpcy5zYTQoYSxudWxsKQpzPWEuY29udGVudApzLnRvU3RyaW5nCkouYlQocykKcj10
+aGlzLnI2KGEsYixudWxsLGMpCmEuY29udGVudC5hcHBlbmRDaGlsZChyKX0sCllDOmZ1bmN0aW9uKGEs
+Yil7cmV0dXJuIHRoaXMucGsoYSxiLG51bGwpfSwKJGl5WToxfQpXLnc2LnByb3RvdHlwZT17fQpXLks1
+LnByb3RvdHlwZT17ClBvOmZ1bmN0aW9uKGEsYixjKXt2YXIgcz1XLlAxKGEub3BlbihiLGMpKQpyZXR1
+cm4gc30sCmdtVzpmdW5jdGlvbihhKXtyZXR1cm4gdC5GLmEoYS5sb2NhdGlvbil9LAp1czpmdW5jdGlv
+bihhLGIpe3JldHVybiBhLmNvbmZpcm0oYil9LAokaUs1OjEsCiRpdjY6MX0KVy5DbS5wcm90b3R5cGU9
+eyRpQ206MX0KVy5DUS5wcm90b3R5cGU9eyRpQ1E6MX0KVy53NC5wcm90b3R5cGU9ewp3OmZ1bmN0aW9u
+KGEpe3ZhciBzLHI9YS5sZWZ0CnIudG9TdHJpbmcKcj0iUmVjdGFuZ2xlICgiK0guRWoocikrIiwgIgpz
+PWEudG9wCnMudG9TdHJpbmcKcz1yK0guRWoocykrIikgIgpyPWEud2lkdGgKci50b1N0cmluZwpyPXMr
+SC5FaihyKSsiIHggIgpzPWEuaGVpZ2h0CnMudG9TdHJpbmcKcmV0dXJuIHIrSC5FaihzKX0sCkROOmZ1
+bmN0aW9uKGEsYil7dmFyIHMscgppZihiPT1udWxsKXJldHVybiExCmlmKHQucS5iKGIpKXtzPWEubGVm
+dApzLnRvU3RyaW5nCnI9Yi5sZWZ0CnIudG9TdHJpbmcKaWYocz09PXIpe3M9YS50b3AKcy50b1N0cmlu
+ZwpyPWIudG9wCnIudG9TdHJpbmcKaWYocz09PXIpe3M9YS53aWR0aApzLnRvU3RyaW5nCnI9Yi53aWR0
+aApyLnRvU3RyaW5nCmlmKHM9PT1yKXtzPWEuaGVpZ2h0CnMudG9TdHJpbmcKcj1iLmhlaWdodApyLnRv
+U3RyaW5nCnI9cz09PXIKcz1yfWVsc2Ugcz0hMX1lbHNlIHM9ITF9ZWxzZSBzPSExfWVsc2Ugcz0hMQpy
+ZXR1cm4gc30sCmdpTzpmdW5jdGlvbihhKXt2YXIgcyxyLHEscD1hLmxlZnQKcC50b1N0cmluZwpwPUMu
+Q0QuZ2lPKHApCnM9YS50b3AKcy50b1N0cmluZwpzPUMuQ0QuZ2lPKHMpCnI9YS53aWR0aApyLnRvU3Ry
+aW5nCnI9Qy5DRC5naU8ocikKcT1hLmhlaWdodApxLnRvU3RyaW5nCnJldHVybiBXLnJFKHAscyxyLEMu
+Q0QuZ2lPKHEpKX19ClcucmgucHJvdG90eXBlPXsKZ0E6ZnVuY3Rpb24oYSl7cmV0dXJuIGEubGVuZ3Ro
+fSwKcTpmdW5jdGlvbihhLGIpe0guSVooYikKaWYoYj4+PjAhPT1ifHxiPj1hLmxlbmd0aCl0aHJvdyBI
+LmIoUC5DZihiLGEsbnVsbCxudWxsLG51bGwpKQpyZXR1cm4gYVtiXX0sClk1OmZ1bmN0aW9uKGEsYixj
+KXt0LkEuYShjKQp0aHJvdyBILmIoUC5MNCgiQ2Fubm90IGFzc2lnbiBlbGVtZW50IG9mIGltbXV0YWJs
+ZSBMaXN0LiIpKX0sCkU6ZnVuY3Rpb24oYSxiKXtpZihiPDB8fGI+PWEubGVuZ3RoKXJldHVybiBILk9I
+KGEsYikKcmV0dXJuIGFbYl19LAokaWJROjEsCiRpWGo6MSwKJGljWDoxLAokaXpNOjF9ClcuY2YucHJv
+dG90eXBlPXsKSzpmdW5jdGlvbihhLGIpe3ZhciBzLHIscSxwLG8KdC5lQS5hKGIpCmZvcihzPXRoaXMu
+Z3ZjKCkscj1zLmxlbmd0aCxxPXRoaXMuYSxwPTA7cDxzLmxlbmd0aDtzLmxlbmd0aD09PXJ8fCgwLEgu
+bGspKHMpLCsrcCl7bz1zW3BdCmIuJDIobyxILm4ocS5nZXRBdHRyaWJ1dGUobykpKX19LApndmM6ZnVu
+Y3Rpb24oKXt2YXIgcyxyLHEscCxvLG4sbT10aGlzLmEuYXR0cmlidXRlcwptLnRvU3RyaW5nCnM9SC5R
+SShbXSx0LnMpCmZvcihyPW0ubGVuZ3RoLHE9dC5oOSxwPTA7cDxyOysrcCl7aWYocD49bS5sZW5ndGgp
+cmV0dXJuIEguT0gobSxwKQpvPXEuYShtW3BdKQppZihvLm5hbWVzcGFjZVVSST09bnVsbCl7bj1vLm5h
+bWUKbi50b1N0cmluZwpDLk5tLmkocyxuKX19cmV0dXJuIHN9LApnbDA6ZnVuY3Rpb24oYSl7cmV0dXJu
+IHRoaXMuZ3ZjKCkubGVuZ3RoPT09MH19ClcuaTcucHJvdG90eXBlPXsKeDQ6ZnVuY3Rpb24oYSl7dmFy
+IHM9dGhpcy5hLmhhc0F0dHJpYnV0ZShhKQpyZXR1cm4gc30sCnE6ZnVuY3Rpb24oYSxiKXtyZXR1cm4g
+dGhpcy5hLmdldEF0dHJpYnV0ZShILm4oYikpfSwKWTU6ZnVuY3Rpb24oYSxiLGMpe3RoaXMuYS5zZXRB
+dHRyaWJ1dGUoYixjKX0sCmdBOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmd2YygpLmxlbmd0aH19Clcu
+U3kucHJvdG90eXBlPXsKeDQ6ZnVuY3Rpb24oYSl7dmFyIHM9dGhpcy5hLmEuaGFzQXR0cmlidXRlKCJk
+YXRhLSIrdGhpcy5PVShhKSkKcmV0dXJuIHN9LApxOmZ1bmN0aW9uKGEsYil7cmV0dXJuIHRoaXMuYS5h
+LmdldEF0dHJpYnV0ZSgiZGF0YS0iK3RoaXMuT1UoSC5uKGIpKSl9LApZNTpmdW5jdGlvbihhLGIsYyl7
+dGhpcy5hLmEuc2V0QXR0cmlidXRlKCJkYXRhLSIrdGhpcy5PVShiKSxjKX0sCks6ZnVuY3Rpb24oYSxi
+KXt0aGlzLmEuSygwLG5ldyBXLktTKHRoaXMsdC5lQS5hKGIpKSl9LApndmM6ZnVuY3Rpb24oKXt2YXIg
+cz1ILlFJKFtdLHQucykKdGhpcy5hLksoMCxuZXcgVy5BMyh0aGlzLHMpKQpyZXR1cm4gc30sCmdBOmZ1
+bmN0aW9uKGEpe3JldHVybiB0aGlzLmd2YygpLmxlbmd0aH0sCmdsMDpmdW5jdGlvbihhKXtyZXR1cm4g
+dGhpcy5ndmMoKS5sZW5ndGg9PT0wfSwKeHE6ZnVuY3Rpb24oYSl7dmFyIHMscixxPUguUUkoYS5zcGxp
+dCgiLSIpLHQucykKZm9yKHM9MTtzPHEubGVuZ3RoOysrcyl7cj1xW3NdCmlmKHIubGVuZ3RoPjApQy5O
+bS5ZNShxLHMsclswXS50b1VwcGVyQ2FzZSgpK0MueEIueW4ociwxKSl9cmV0dXJuIEMuTm0uayhxLCIi
+KX0sCk9VOmZ1bmN0aW9uKGEpe3ZhciBzLHIscSxwLG8KZm9yKHM9YS5sZW5ndGgscj0wLHE9IiI7cjxz
+Oysrcil7cD1hW3JdCm89cC50b0xvd2VyQ2FzZSgpCnE9KHAhPT1vJiZyPjA/cSsiLSI6cSkrb31yZXR1
+cm4gcS5jaGFyQ29kZUF0KDApPT0wP3E6cX19ClcuS1MucHJvdG90eXBlPXsKJDI6ZnVuY3Rpb24oYSxi
+KXtpZihDLnhCLm4oYSwiZGF0YS0iKSl0aGlzLmIuJDIodGhpcy5hLnhxKEMueEIueW4oYSw1KSksYil9
+LAokUzo4fQpXLkEzLnByb3RvdHlwZT17CiQyOmZ1bmN0aW9uKGEsYil7aWYoQy54Qi5uKGEsImRhdGEt
+IikpQy5ObS5pKHRoaXMuYix0aGlzLmEueHEoQy54Qi55bihhLDUpKSl9LAokUzo4fQpXLkk0LnByb3Rv
+dHlwZT17CkQ6ZnVuY3Rpb24oKXt2YXIgcyxyLHEscCxvPVAuTHModC5OKQpmb3Iocz10aGlzLmEuY2xh
+c3NOYW1lLnNwbGl0KCIgIikscj1zLmxlbmd0aCxxPTA7cTxyOysrcSl7cD1KLlQwKHNbcV0pCmlmKHAu
+bGVuZ3RoIT09MClvLmkoMCxwKX1yZXR1cm4gb30sClg6ZnVuY3Rpb24oYSl7dGhpcy5hLmNsYXNzTmFt
+ZT10LkMuYShhKS5rKDAsIiAiKX0sCmdBOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmEuY2xhc3NMaXN0
+Lmxlbmd0aH0sCmdsMDpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5hLmNsYXNzTGlzdC5sZW5ndGg9PT0w
+fSwKZ29yOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmEuY2xhc3NMaXN0Lmxlbmd0aCE9PTB9LApWMTpm
+dW5jdGlvbihhKXt0aGlzLmEuY2xhc3NOYW1lPSIifSwKdGc6ZnVuY3Rpb24oYSxiKXt2YXIgcz10aGlz
+LmEuY2xhc3NMaXN0LmNvbnRhaW5zKGIpCnJldHVybiBzfSwKaTpmdW5jdGlvbihhLGIpe3ZhciBzLHIK
+SC5uKGIpCnM9dGhpcy5hLmNsYXNzTGlzdApyPXMuY29udGFpbnMoYikKcy5hZGQoYikKcmV0dXJuIXJ9
+LApSOmZ1bmN0aW9uKGEsYil7dmFyIHMscixxCmlmKHR5cGVvZiBiPT0ic3RyaW5nIil7cz10aGlzLmEu
+Y2xhc3NMaXN0CnI9cy5jb250YWlucyhiKQpzLnJlbW92ZShiKQpxPXJ9ZWxzZSBxPSExCnJldHVybiBx
+fSwKRlY6ZnVuY3Rpb24oYSxiKXtXLlROKHRoaXMuYSx0Lk8uYShiKSl9fQpXLkZrLnByb3RvdHlwZT17
+fQpXLlJPLnByb3RvdHlwZT17fQpXLkNxLnByb3RvdHlwZT17fQpXLnhDLnByb3RvdHlwZT17fQpXLnZO
+LnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmEuJDEodC5CLmEoYSkpfSwKJFM6
+Mjd9ClcuSlEucHJvdG90eXBlPXsKQ1k6ZnVuY3Rpb24oYSl7dmFyIHMKaWYoJC5vci5hPT09MCl7Zm9y
+KHM9MDtzPDI2MjsrK3MpJC5vci5ZNSgwLEMuY21bc10sVy5wUygpKQpmb3Iocz0wO3M8MTI7KytzKSQu
+b3IuWTUoMCxDLkJJW3NdLFcuVjQoKSl9fSwKaTA6ZnVuY3Rpb24oYSl7cmV0dXJuICQuQU4oKS50Zygw
+LFcuclMoYSkpfSwKRWI6ZnVuY3Rpb24oYSxiLGMpe3ZhciBzPSQub3IucSgwLFcuclMoYSkrIjo6Iiti
+KQppZihzPT1udWxsKXM9JC5vci5xKDAsIio6OiIrYikKaWYocz09bnVsbClyZXR1cm4hMQpyZXR1cm4g
+SC5wOChzLiQ0KGEsYixjLHRoaXMpKX0sCiRpa0Y6MX0KVy5HbS5wcm90b3R5cGU9ewpnbTpmdW5jdGlv
+bihhKXtyZXR1cm4gbmV3IFcuVzkoYSx0aGlzLmdBKGEpLEgueksoYSkuQygiVzk8R20uRT4iKSl9fQpX
+LnZELnByb3RvdHlwZT17CmkwOmZ1bmN0aW9uKGEpe3JldHVybiBDLk5tLlZyKHRoaXMuYSxuZXcgVy5V
+dihhKSl9LApFYjpmdW5jdGlvbihhLGIsYyl7cmV0dXJuIEMuTm0uVnIodGhpcy5hLG5ldyBXLkVnKGEs
+YixjKSl9LAokaWtGOjF9ClcuVXYucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7cmV0dXJuIHQuRS5h
+KGEpLmkwKHRoaXMuYSl9LAokUzoxNn0KVy5FZy5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXtyZXR1
+cm4gdC5FLmEoYSkuRWIodGhpcy5hLHRoaXMuYix0aGlzLmMpfSwKJFM6MTZ9ClcubTYucHJvdG90eXBl
+PXsKQ1k6ZnVuY3Rpb24oYSxiLGMsZCl7dmFyIHMscixxCnRoaXMuYS5GVigwLGMpCnM9Yi5ldigwLG5l
+dyBXLkVvKCkpCnI9Yi5ldigwLG5ldyBXLldrKCkpCnRoaXMuYi5GVigwLHMpCnE9dGhpcy5jCnEuRlYo
+MCxDLnhEKQpxLkZWKDAscil9LAppMDpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5hLnRnKDAsVy5yUyhh
+KSl9LApFYjpmdW5jdGlvbihhLGIsYyl7dmFyIHM9dGhpcyxyPVcuclMoYSkscT1zLmMKaWYocS50Zygw
+LHIrIjo6IitiKSlyZXR1cm4gcy5kLkR0KGMpCmVsc2UgaWYocS50ZygwLCIqOjoiK2IpKXJldHVybiBz
+LmQuRHQoYykKZWxzZXtxPXMuYgppZihxLnRnKDAscisiOjoiK2IpKXJldHVybiEwCmVsc2UgaWYocS50
+ZygwLCIqOjoiK2IpKXJldHVybiEwCmVsc2UgaWYocS50ZygwLHIrIjo6KiIpKXJldHVybiEwCmVsc2Ug
+aWYocS50ZygwLCIqOjoqIikpcmV0dXJuITB9cmV0dXJuITF9LAokaWtGOjF9ClcuRW8ucHJvdG90eXBl
+PXsKJDE6ZnVuY3Rpb24oYSl7cmV0dXJuIUMuTm0udGcoQy5CSSxILm4oYSkpfSwKJFM6Nn0KVy5Xay5w
+cm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXtyZXR1cm4gQy5ObS50ZyhDLkJJLEgubihhKSl9LAokUzo2
+fQpXLmN0LnByb3RvdHlwZT17CkViOmZ1bmN0aW9uKGEsYixjKXtpZih0aGlzLmpGKGEsYixjKSlyZXR1
+cm4hMAppZihiPT09InRlbXBsYXRlIiYmYz09PSIiKXJldHVybiEwCmlmKGEuZ2V0QXR0cmlidXRlKCJ0
+ZW1wbGF0ZSIpPT09IiIpcmV0dXJuIHRoaXMuZS50ZygwLGIpCnJldHVybiExfX0KVy5JQS5wcm90b3R5
+cGU9ewokMTpmdW5jdGlvbihhKXtyZXR1cm4iVEVNUExBVEU6OiIrSC5uKGEpfSwKJFM6Mn0KVy5Pdy5w
+cm90b3R5cGU9ewppMDpmdW5jdGlvbihhKXt2YXIgcwppZih0LmFPLmIoYSkpcmV0dXJuITEKcz10Lmc3
+LmIoYSkKaWYocyYmVy5yUyhhKT09PSJmb3JlaWduT2JqZWN0IilyZXR1cm4hMQppZihzKXJldHVybiEw
+CnJldHVybiExfSwKRWI6ZnVuY3Rpb24oYSxiLGMpe2lmKGI9PT0iaXMifHxDLnhCLm4oYiwib24iKSly
+ZXR1cm4hMQpyZXR1cm4gdGhpcy5pMChhKX0sCiRpa0Y6MX0KVy5XOS5wcm90b3R5cGU9ewpGOmZ1bmN0
+aW9uKCl7dmFyIHM9dGhpcyxyPXMuYysxLHE9cy5iCmlmKHI8cSl7cy5zcChKLng5KHMuYSxyKSkKcy5j
+PXIKcmV0dXJuITB9cy5zcChudWxsKQpzLmM9cQpyZXR1cm4hMX0sCmdsOmZ1bmN0aW9uKCl7cmV0dXJu
+IHRoaXMuJHRpLmMuYSh0aGlzLmQpfSwKc3A6ZnVuY3Rpb24oYSl7dGhpcy5kPXRoaXMuJHRpLkMoIjE/
+IikuYShhKX0sCiRpQW46MX0KVy5kVy5wcm90b3R5cGU9eyRpRDA6MSwkaXY2OjF9ClcubWsucHJvdG90
+eXBlPXskaXkwOjF9ClcuS28ucHJvdG90eXBlPXsKUG46ZnVuY3Rpb24oYSl7dmFyIHMscj1uZXcgVy5m
+bSh0aGlzKQpkb3tzPXRoaXMuYgpyLiQyKGEsbnVsbCl9d2hpbGUocyE9PXRoaXMuYil9LApFUDpmdW5j
+dGlvbihhLGIpeysrdGhpcy5iCmlmKGI9PW51bGx8fGIhPT1hLnBhcmVudE5vZGUpSi5MdChhKQplbHNl
+IGIucmVtb3ZlQ2hpbGQoYSl9LApJNDpmdW5jdGlvbihhLGIpe3ZhciBzLHIscSxwLG8sbj0hMCxtPW51
+bGwsbD1udWxsCnRyeXttPUouaWcoYSkKbD1tLmEuZ2V0QXR0cmlidXRlKCJpcyIpCnQuaC5hKGEpCnM9
+ZnVuY3Rpb24oYyl7aWYoIShjLmF0dHJpYnV0ZXMgaW5zdGFuY2VvZiBOYW1lZE5vZGVNYXApKXJldHVy
+biB0cnVlCmlmKGMuaWQ9PSJsYXN0Q2hpbGQifHxjLm5hbWU9PSJsYXN0Q2hpbGQifHxjLmlkPT0icHJl
+dmlvdXNTaWJsaW5nInx8Yy5uYW1lPT0icHJldmlvdXNTaWJsaW5nInx8Yy5pZD09ImNoaWxkcmVuInx8
+Yy5uYW1lPT0iY2hpbGRyZW4iKXJldHVybiB0cnVlCnZhciBrPWMuY2hpbGROb2RlcwppZihjLmxhc3RD
+aGlsZCYmYy5sYXN0Q2hpbGQhPT1rW2subGVuZ3RoLTFdKXJldHVybiB0cnVlCmlmKGMuY2hpbGRyZW4p
+aWYoIShjLmNoaWxkcmVuIGluc3RhbmNlb2YgSFRNTENvbGxlY3Rpb258fGMuY2hpbGRyZW4gaW5zdGFu
+Y2VvZiBOb2RlTGlzdCkpcmV0dXJuIHRydWUKdmFyIGo9MAppZihjLmNoaWxkcmVuKWo9Yy5jaGlsZHJl
+bi5sZW5ndGgKZm9yKHZhciBpPTA7aTxqO2krKyl7dmFyIGg9Yy5jaGlsZHJlbltpXQppZihoLmlkPT0i
+YXR0cmlidXRlcyJ8fGgubmFtZT09ImF0dHJpYnV0ZXMifHxoLmlkPT0ibGFzdENoaWxkInx8aC5uYW1l
+PT0ibGFzdENoaWxkInx8aC5pZD09InByZXZpb3VzU2libGluZyJ8fGgubmFtZT09InByZXZpb3VzU2li
+bGluZyJ8fGguaWQ9PSJjaGlsZHJlbiJ8fGgubmFtZT09ImNoaWxkcmVuIilyZXR1cm4gdHJ1ZX1yZXR1
+cm4gZmFsc2V9KGEpCm49SC5vVChzKT8hMDohKGEuYXR0cmlidXRlcyBpbnN0YW5jZW9mIE5hbWVkTm9k
+ZU1hcCl9Y2F0Y2gocCl7SC5SdShwKX1yPSJlbGVtZW50IHVucHJpbnRhYmxlIgp0cnl7cj1KLncoYSl9
+Y2F0Y2gocCl7SC5SdShwKX10cnl7cT1XLnJTKGEpCnRoaXMua1IodC5oLmEoYSksYixuLHIscSx0LmYu
+YShtKSxILmsobCkpfWNhdGNoKHApe2lmKEguUnUocCkgaW5zdGFuY2VvZiBQLkFUKXRocm93IHAKZWxz
+ZXt0aGlzLkVQKGEsYikKd2luZG93Cm89IlJlbW92aW5nIGNvcnJ1cHRlZCBlbGVtZW50ICIrSC5Faihy
+KQppZih0eXBlb2YgY29uc29sZSE9InVuZGVmaW5lZCIpd2luZG93LmNvbnNvbGUud2FybihvKX19fSwK
+a1I6ZnVuY3Rpb24oYSxiLGMsZCxlLGYsZyl7dmFyIHMscixxLHAsbyxuLG09dGhpcwppZihjKXttLkVQ
+KGEsYikKd2luZG93CnM9IlJlbW92aW5nIGVsZW1lbnQgZHVlIHRvIGNvcnJ1cHRlZCBhdHRyaWJ1dGVz
+IG9uIDwiK2QrIj4iCmlmKHR5cGVvZiBjb25zb2xlIT0idW5kZWZpbmVkIil3aW5kb3cuY29uc29sZS53
+YXJuKHMpCnJldHVybn1pZighbS5hLmkwKGEpKXttLkVQKGEsYikKd2luZG93CnM9IlJlbW92aW5nIGRp
+c2FsbG93ZWQgZWxlbWVudCA8IitlKyI+IGZyb20gIitILkVqKGIpCmlmKHR5cGVvZiBjb25zb2xlIT0i
+dW5kZWZpbmVkIil3aW5kb3cuY29uc29sZS53YXJuKHMpCnJldHVybn1pZihnIT1udWxsKWlmKCFtLmEu
+RWIoYSwiaXMiLGcpKXttLkVQKGEsYikKd2luZG93CnM9IlJlbW92aW5nIGRpc2FsbG93ZWQgdHlwZSBl
+eHRlbnNpb24gPCIrZSsnIGlzPSInK2crJyI+JwppZih0eXBlb2YgY29uc29sZSE9InVuZGVmaW5lZCIp
+d2luZG93LmNvbnNvbGUud2FybihzKQpyZXR1cm59cz1mLmd2YygpCnI9SC5RSShzLnNsaWNlKDApLEgu
+dDYocykpCmZvcihxPWYuZ3ZjKCkubGVuZ3RoLTEscz1mLmE7cT49MDstLXEpe2lmKHE+PXIubGVuZ3Ro
+KXJldHVybiBILk9IKHIscSkKcD1yW3FdCm89bS5hCm49Si5jSChwKQpILm4ocCkKaWYoIW8uRWIoYSxu
+LEgubihzLmdldEF0dHJpYnV0ZShwKSkpKXt3aW5kb3cKbz0iUmVtb3ZpbmcgZGlzYWxsb3dlZCBhdHRy
+aWJ1dGUgPCIrZSsiICIrcCsnPSInK0guRWoocy5nZXRBdHRyaWJ1dGUocCkpKyciPicKaWYodHlwZW9m
+IGNvbnNvbGUhPSJ1bmRlZmluZWQiKXdpbmRvdy5jb25zb2xlLndhcm4obykKcy5yZW1vdmVBdHRyaWJ1
+dGUocCl9fWlmKHQuYVcuYihhKSl7cz1hLmNvbnRlbnQKcy50b1N0cmluZwptLlBuKHMpfX0sCiRpb246
+MX0KVy5mbS5wcm90b3R5cGU9ewokMjpmdW5jdGlvbihhLGIpe3ZhciBzLHIscSxwLG8sbixtPXRoaXMu
+YQpzd2l0Y2goYS5ub2RlVHlwZSl7Y2FzZSAxOm0uSTQoYSxiKQpicmVhawpjYXNlIDg6Y2FzZSAxMTpj
+YXNlIDM6Y2FzZSA0OmJyZWFrCmRlZmF1bHQ6bS5FUChhLGIpfXM9YS5sYXN0Q2hpbGQKZm9yKHE9dC5B
+O3MhPW51bGw7KXtyPW51bGwKdHJ5e3I9cy5wcmV2aW91c1NpYmxpbmcKaWYociE9bnVsbCl7cD1yLm5l
+eHRTaWJsaW5nCm89cwpvPXA9PW51bGw/byE9bnVsbDpwIT09bwpwPW99ZWxzZSBwPSExCmlmKHApe3A9
+UC5QVigiQ29ycnVwdCBIVE1MIikKdGhyb3cgSC5iKHApfX1jYXRjaChuKXtILlJ1KG4pCnA9cS5hKHMp
+OysrbS5iCm89cC5wYXJlbnROb2RlCmlmKGEhPT1vKXtpZihvIT1udWxsKW8ucmVtb3ZlQ2hpbGQocCl9
+ZWxzZSBhLnJlbW92ZUNoaWxkKHApCnM9bnVsbApyPWEubGFzdENoaWxkfWlmKHMhPW51bGwpdGhpcy4k
+MihzLGEpCnM9cn19LAokUzozMH0KVy5MZS5wcm90b3R5cGU9e30KVy5LNy5wcm90b3R5cGU9e30KVy5y
+Qi5wcm90b3R5cGU9e30KVy5YVy5wcm90b3R5cGU9e30KVy5vYS5wcm90b3R5cGU9e30KUC5pSi5wcm90
+b3R5cGU9ewpWSDpmdW5jdGlvbihhKXt2YXIgcyxyPXRoaXMuYSxxPXIubGVuZ3RoCmZvcihzPTA7czxx
+OysrcylpZihyW3NdPT09YSlyZXR1cm4gcwpDLk5tLmkocixhKQpDLk5tLmkodGhpcy5iLG51bGwpCnJl
+dHVybiBxfSwKUHY6ZnVuY3Rpb24oYSl7dmFyIHMscixxLHA9dGhpcyxvPXt9CmlmKGE9PW51bGwpcmV0
+dXJuIGEKaWYoSC5yUShhKSlyZXR1cm4gYQppZih0eXBlb2YgYT09Im51bWJlciIpcmV0dXJuIGEKaWYo
+dHlwZW9mIGE9PSJzdHJpbmciKXJldHVybiBhCmlmKGEgaW5zdGFuY2VvZiBQLmlQKXJldHVybiBuZXcg
+RGF0ZShhLmEpCmlmKHQuZnYuYihhKSl0aHJvdyBILmIoUC5TWSgic3RydWN0dXJlZCBjbG9uZSBvZiBS
+ZWdFeHAiKSkKaWYodC5jOC5iKGEpKXJldHVybiBhCmlmKHQudy5iKGEpKXJldHVybiBhCmlmKHQuSS5i
+KGEpKXJldHVybiBhCnM9dC5kRS5iKGEpfHwhMQppZihzKXJldHVybiBhCmlmKHQuZi5iKGEpKXtyPXAu
+VkgoYSkKcz1wLmIKaWYocj49cy5sZW5ndGgpcmV0dXJuIEguT0gocyxyKQpxPW8uYT1zW3JdCmlmKHEh
+PW51bGwpcmV0dXJuIHEKcT17fQpvLmE9cQpDLk5tLlk1KHMscixxKQphLksoMCxuZXcgUC5FMihvLHAp
+KQpyZXR1cm4gby5hfWlmKHQuai5iKGEpKXtyPXAuVkgoYSkKbz1wLmIKaWYocj49by5sZW5ndGgpcmV0
+dXJuIEguT0gobyxyKQpxPW9bcl0KaWYocSE9bnVsbClyZXR1cm4gcQpyZXR1cm4gcC5layhhLHIpfWlm
+KHQuZUguYihhKSl7cj1wLlZIKGEpCnM9cC5iCmlmKHI+PXMubGVuZ3RoKXJldHVybiBILk9IKHMscikK
+cT1vLmI9c1tyXQppZihxIT1udWxsKXJldHVybiBxCnE9e30Kby5iPXEKQy5ObS5ZNShzLHIscSkKcC5p
+bShhLG5ldyBQLmpnKG8scCkpCnJldHVybiBvLmJ9dGhyb3cgSC5iKFAuU1koInN0cnVjdHVyZWQgY2xv
+bmUgb2Ygb3RoZXIgdHlwZSIpKX0sCmVrOmZ1bmN0aW9uKGEsYil7dmFyIHMscj1KLlU2KGEpLHE9ci5n
+QShhKSxwPW5ldyBBcnJheShxKQpDLk5tLlk1KHRoaXMuYixiLHApCmZvcihzPTA7czxxOysrcylDLk5t
+Llk1KHAscyx0aGlzLlB2KHIucShhLHMpKSkKcmV0dXJuIHB9fQpQLkUyLnByb3RvdHlwZT17CiQyOmZ1
+bmN0aW9uKGEsYil7dGhpcy5hLmFbYV09dGhpcy5iLlB2KGIpfSwKJFM6MzF9ClAuamcucHJvdG90eXBl
+PXsKJDI6ZnVuY3Rpb24oYSxiKXt0aGlzLmEuYlthXT10aGlzLmIuUHYoYil9LAokUzozMn0KUC5CZi5w
+cm90b3R5cGU9ewppbTpmdW5jdGlvbihhLGIpe3ZhciBzLHIscSxwCnQuYjguYShiKQpmb3Iocz1PYmpl
+Y3Qua2V5cyhhKSxyPXMubGVuZ3RoLHE9MDtxPHI7KytxKXtwPXNbcV0KYi4kMihwLGFbcF0pfX19ClAu
+QXMucHJvdG90eXBlPXsKVjpmdW5jdGlvbihhKXt2YXIgcwpILm4oYSkKcz0kLmhHKCkuYgppZihzLnRl
+c3QoYSkpcmV0dXJuIGEKdGhyb3cgSC5iKFAuTDMoYSwidmFsdWUiLCJOb3QgYSB2YWxpZCBjbGFzcyB0
+b2tlbiIpKX0sCnc6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuRCgpLmsoMCwiICIpfSwKZ206ZnVuY3Rp
+b24oYSl7dmFyIHM9dGhpcy5EKCkKcmV0dXJuIFAucmoocyxzLnIsSC5MaChzKS5jKX0sCmdsMDpmdW5j
+dGlvbihhKXtyZXR1cm4gdGhpcy5EKCkuYT09PTB9LApnb3I6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMu
+RCgpLmEhPT0wfSwKZ0E6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuRCgpLmF9LAp0ZzpmdW5jdGlvbihh
+LGIpe3RoaXMuVihiKQpyZXR1cm4gdGhpcy5EKCkudGcoMCxiKX0sCmk6ZnVuY3Rpb24oYSxiKXt2YXIg
+cwpILm4oYikKdGhpcy5WKGIpCnM9dGhpcy5PUyhuZXcgUC5HRShiKSkKcmV0dXJuIEgucDgocz09bnVs
+bD8hMTpzKX0sClI6ZnVuY3Rpb24oYSxiKXt2YXIgcyxyCmlmKHR5cGVvZiBiIT0ic3RyaW5nIilyZXR1
+cm4hMQp0aGlzLlYoYikKcz10aGlzLkQoKQpyPXMuUigwLGIpCnRoaXMuWChzKQpyZXR1cm4gcn0sCkZW
+OmZ1bmN0aW9uKGEsYil7dGhpcy5PUyhuZXcgUC5ONyh0aGlzLHQuTy5hKGIpKSl9LAplUjpmdW5jdGlv
+bihhLGIpe3ZhciBzPXRoaXMuRCgpCnJldHVybiBILmJLKHMsYixILkxoKHMpLkMoImxmLkUiKSl9LApF
+OmZ1bmN0aW9uKGEsYil7cmV0dXJuIHRoaXMuRCgpLkUoMCxiKX0sClYxOmZ1bmN0aW9uKGEpe3RoaXMu
+T1MobmV3IFAudVEoKSl9LApPUzpmdW5jdGlvbihhKXt2YXIgcyxyCnQuYlUuYShhKQpzPXRoaXMuRCgp
+CnI9YS4kMShzKQp0aGlzLlgocykKcmV0dXJuIHJ9fQpQLkdFLnByb3RvdHlwZT17CiQxOmZ1bmN0aW9u
+KGEpe3JldHVybiB0LkMuYShhKS5pKDAsdGhpcy5hKX0sCiRTOjMzfQpQLk43LnByb3RvdHlwZT17CiQx
+OmZ1bmN0aW9uKGEpe3ZhciBzPXRoaXMuYixyPUgudDYocykKcmV0dXJuIHQuQy5hKGEpLkZWKDAsbmV3
+IEgubEoocyxyLkMoInFVKDEpIikuYSh0aGlzLmEuZ3VNKCkpLHIuQygibEo8MSxxVT4iKSkpfSwKJFM6
+MTJ9ClAudVEucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7dC5DLmEoYSkKaWYoYS5hPjApe2EuYj1h
+LmM9YS5kPWEuZT1hLmY9bnVsbAphLmE9MAphLlMoKX1yZXR1cm4gbnVsbH0sCiRTOjEyfQpQLmhGLnBy
+b3RvdHlwZT17JGloRjoxfQpQLlBDLnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3ZhciBzCnQuWS5h
+KGEpCnM9ZnVuY3Rpb24oYixjLGQpe3JldHVybiBmdW5jdGlvbigpe3JldHVybiBiKGMsZCx0aGlzLEFy
+cmF5LnByb3RvdHlwZS5zbGljZS5hcHBseShhcmd1bWVudHMpKX19KFAuUjQsYSwhMSkKUC5EbShzLCQu
+eigpLGEpCnJldHVybiBzfSwKJFM6NH0KUC5tdC5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXtyZXR1
+cm4gbmV3IHRoaXMuYShhKX0sCiRTOjR9ClAuUVMucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7cmV0
+dXJuIG5ldyBQLnI3KHQuSy5hKGEpKX0sCiRTOjQ0fQpQLm5wLnByb3RvdHlwZT17CiQxOmZ1bmN0aW9u
+KGEpe3JldHVybiBuZXcgUC5Ueih0LksuYShhKSx0LmFtKX0sCiRTOjM2fQpQLlV0LnByb3RvdHlwZT17
+CiQxOmZ1bmN0aW9uKGEpe3JldHVybiBuZXcgUC5FNCh0LksuYShhKSl9LAokUzozN30KUC5FNC5wcm90
+b3R5cGU9ewpxOmZ1bmN0aW9uKGEsYil7aWYodHlwZW9mIGIhPSJzdHJpbmciJiZ0eXBlb2YgYiE9Im51
+bWJlciIpdGhyb3cgSC5iKFAueFkoInByb3BlcnR5IGlzIG5vdCBhIFN0cmluZyBvciBudW0iKSkKcmV0
+dXJuIFAuZFUodGhpcy5hW2JdKX0sClk1OmZ1bmN0aW9uKGEsYixjKXtpZih0eXBlb2YgYiE9InN0cmlu
+ZyImJnR5cGVvZiBiIT0ibnVtYmVyIil0aHJvdyBILmIoUC54WSgicHJvcGVydHkgaXMgbm90IGEgU3Ry
+aW5nIG9yIG51bSIpKQp0aGlzLmFbYl09UC53WShjKX0sCkROOmZ1bmN0aW9uKGEsYil7aWYoYj09bnVs
+bClyZXR1cm4hMQpyZXR1cm4gYiBpbnN0YW5jZW9mIFAuRTQmJnRoaXMuYT09PWIuYX0sCnc6ZnVuY3Rp
+b24oYSl7dmFyIHMscgp0cnl7cz1TdHJpbmcodGhpcy5hKQpyZXR1cm4gc31jYXRjaChyKXtILlJ1KHIp
+CnM9dGhpcy54YigwKQpyZXR1cm4gc319LApWNzpmdW5jdGlvbihhLGIpe3ZhciBzLHI9dGhpcy5hCmlm
+KGI9PW51bGwpcz1udWxsCmVsc2V7cz1ILnQ2KGIpCnM9UC5DSChuZXcgSC5sSihiLHMuQygiQCgxKSIp
+LmEoUC5pRygpKSxzLkMoImxKPDEsQD4iKSksITAsdC56KX1yZXR1cm4gUC5kVShyW2FdLmFwcGx5KHIs
+cykpfSwKZ2lPOmZ1bmN0aW9uKGEpe3JldHVybiAwfX0KUC5yNy5wcm90b3R5cGU9e30KUC5Uei5wcm90
+b3R5cGU9ewpjUDpmdW5jdGlvbihhKXt2YXIgcz10aGlzLHI9YTwwfHxhPj1zLmdBKHMpCmlmKHIpdGhy
+b3cgSC5iKFAuVEUoYSwwLHMuZ0EocyksbnVsbCxudWxsKSl9LApxOmZ1bmN0aW9uKGEsYil7aWYoSC5v
+ayhiKSl0aGlzLmNQKGIpCnJldHVybiB0aGlzLiR0aS5jLmEodGhpcy5VcigwLGIpKX0sClk1OmZ1bmN0
+aW9uKGEsYixjKXt0aGlzLmNQKGIpCnRoaXMuYmgoMCxiLGMpfSwKZ0E6ZnVuY3Rpb24oYSl7dmFyIHM9
+dGhpcy5hLmxlbmd0aAppZih0eXBlb2Ygcz09PSJudW1iZXIiJiZzPj4+MD09PXMpcmV0dXJuIHMKdGhy
+b3cgSC5iKFAuUFYoIkJhZCBKc0FycmF5IGxlbmd0aCIpKX0sCiRpYlE6MSwKJGljWDoxLAokaXpNOjF9
+ClAudmcucHJvdG90eXBlPXsKWTU6ZnVuY3Rpb24oYSxiLGMpe3JldHVybiB0aGlzLmU0KDAsYixjKX19
+ClAubmQucHJvdG90eXBlPXskaW5kOjF9ClAuS2UucHJvdG90eXBlPXsKRDpmdW5jdGlvbigpe3ZhciBz
+LHIscSxwLG89dGhpcy5hLmdldEF0dHJpYnV0ZSgiY2xhc3MiKSxuPVAuTHModC5OKQppZihvPT1udWxs
+KXJldHVybiBuCmZvcihzPW8uc3BsaXQoIiAiKSxyPXMubGVuZ3RoLHE9MDtxPHI7KytxKXtwPUouVDAo
+c1txXSkKaWYocC5sZW5ndGghPT0wKW4uaSgwLHApfXJldHVybiBufSwKWDpmdW5jdGlvbihhKXt0aGlz
+LmEuc2V0QXR0cmlidXRlKCJjbGFzcyIsYS5rKDAsIiAiKSl9fQpQLmhpLnByb3RvdHlwZT17CmdQOmZ1
+bmN0aW9uKGEpe3JldHVybiBuZXcgUC5LZShhKX0sCnNoZjpmdW5jdGlvbihhLGIpe3RoaXMuWUMoYSxi
+KX0sCnI2OmZ1bmN0aW9uKGEsYixjLGQpe3ZhciBzLHIscSxwLG8sbgppZihkPT1udWxsKXtzPUguUUko
+W10sdC5yKQpkPW5ldyBXLnZEKHMpCkMuTm0uaShzLFcuVHcobnVsbCkpCkMuTm0uaShzLFcuQmwoKSkK
+Qy5ObS5pKHMsbmV3IFcuT3coKSl9Yz1uZXcgVy5LbyhkKQpyPSc8c3ZnIHZlcnNpb249IjEuMSI+JytI
+LkVqKGIpKyI8L3N2Zz4iCnM9ZG9jdW1lbnQKcT1zLmJvZHkKcS50b1N0cmluZwpwPUMuUlkuQUgocSxy
+LGMpCm89cy5jcmVhdGVEb2N1bWVudEZyYWdtZW50KCkKcz1uZXcgVy5lNyhwKQpuPXMuZ3I4KHMpCmZv
+cig7cz1uLmZpcnN0Q2hpbGQscyE9bnVsbDspby5hcHBlbmRDaGlsZChzKQpyZXR1cm4gb30sCm56OmZ1
+bmN0aW9uKGEsYixjLGQsZSl7dGhyb3cgSC5iKFAuTDQoIkNhbm5vdCBpbnZva2UgaW5zZXJ0QWRqYWNl
+bnRIdG1sIG9uIFNWRy4iKSl9LApnVmw6ZnVuY3Rpb24oYSl7cmV0dXJuIG5ldyBXLkNxKGEsImNsaWNr
+IiwhMSx0LlEpfSwKJGloaToxfQpVLmQyLnByb3RvdHlwZT17Ckx0OmZ1bmN0aW9uKCl7dmFyIHMscixx
+LHAsbz10aGlzLG49dC5OLG09dC5YLGw9UC5GbChuLG0pLGs9by5hCmlmKGshPW51bGwpe3M9SC5RSShb
+XSx0LmQpCmZvcihyPWsubGVuZ3RoLHE9MDtxPGsubGVuZ3RoO2subGVuZ3RoPT09cnx8KDAsSC5sayko
+ayksKytxKXtwPWtbcV0Kcy5wdXNoKFAuRUYoWyJkZXNjcmlwdGlvbiIscC5hLCJocmVmIixwLmJdLG4s
+bSkpfWwuWTUoMCwiZWRpdHMiLHMpfWwuWTUoMCwiZXhwbGFuYXRpb24iLG8uYikKbC5ZNSgwLCJsaW5l
+IixvLmMpCmwuWTUoMCwiZGlzcGxheVBhdGgiLG8uZCkKbC5ZNSgwLCJ1cmlQYXRoIixvLmUpCm49by5m
+CmlmKG4hPW51bGwpe209SC5RSShbXSx0LmQpCmZvcihrPW4ubGVuZ3RoLHE9MDtxPG4ubGVuZ3RoO24u
+bGVuZ3RoPT09a3x8KDAsSC5saykobiksKytxKW0ucHVzaChuW3FdLkx0KCkpCmwuWTUoMCwidHJhY2Vz
+IixtKX1yZXR1cm4gbH19ClUuU2UucHJvdG90eXBlPXsKTHQ6ZnVuY3Rpb24oKXtyZXR1cm4gUC5FRihb
+ImRlc2NyaXB0aW9uIix0aGlzLmEsImhyZWYiLHRoaXMuYl0sdC5OLHQuWCl9fQpVLk1sLnByb3RvdHlw
+ZT17Ckx0OmZ1bmN0aW9uKCl7cmV0dXJuIFAuRUYoWyJocmVmIix0aGlzLmEsImxpbmUiLHRoaXMuYiwi
+cGF0aCIsdGhpcy5jXSx0Lk4sdC5YKX19ClUueUQucHJvdG90eXBlPXsKTHQ6ZnVuY3Rpb24oKXt2YXIg
+cyxyLHEscD1ILlFJKFtdLHQuZCkKZm9yKHM9dGhpcy5iLHI9cy5sZW5ndGgscT0wO3E8cy5sZW5ndGg7
+cy5sZW5ndGg9PT1yfHwoMCxILmxrKShzKSwrK3EpcC5wdXNoKHNbcV0uTHQoKSkKcmV0dXJuIFAuRUYo
+WyJkZXNjcmlwdGlvbiIsdGhpcy5hLCJlbnRyaWVzIixwXSx0Lk4sdC5YKX19ClUud2IucHJvdG90eXBl
+PXsKTHQ6ZnVuY3Rpb24oKXt2YXIgcyxyLHEscD10aGlzLG89UC5GbCh0Lk4sdC5YKQpvLlk1KDAsImRl
+c2NyaXB0aW9uIixwLmEpCnM9cC5iCmlmKHMhPW51bGwpby5ZNSgwLCJmdW5jdGlvbiIscykKcz1wLmMK
+aWYocyE9bnVsbClvLlk1KDAsImxpbmsiLHMuTHQoKSkKcz1wLmQKaWYocy5sZW5ndGghPT0wKXtyPUgu
+dDYocykKcT1yLkMoImxKPDEsWjA8cVUsTWg/Pj4iKQpvLlk1KDAsImhpbnRBY3Rpb25zIixQLlkxKG5l
+dyBILmxKKHMsci5DKCJaMDxxVSxNaD8+KDEpIikuYShuZXcgVS5iMCgpKSxxKSwhMCxxLkMoImFMLkUi
+KSkpfXJldHVybiBvfX0KVS5hTi5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXtyZXR1cm4gUi5ueih0
+LkcuYShhKSl9LAokUzozOH0KVS5iMC5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXtyZXR1cm4gdC5K
+LmEoYSkuTHQoKX0sCiRTOjM5fQpCLmo4LnByb3RvdHlwZT17Ckx0OmZ1bmN0aW9uKCl7cmV0dXJuIFAu
+RUYoWyJsaW5lIix0aGlzLmEsImV4cGxhbmF0aW9uIix0aGlzLmIsIm9mZnNldCIsdGhpcy5jXSx0Lk4s
+dC5YKX19CkIucXAucHJvdG90eXBlPXsKTHQ6ZnVuY3Rpb24oKXt2YXIgcyxyLHEscCxvLG4sbSxsPXRo
+aXMsaz10Lk4saj1QLkZsKGssdC54KQpmb3Iocz1sLmQscz1zLmdQdShzKSxzPXMuZ20ocykscj10Llgs
+cT10LmQ7cy5GKCk7KXtwPXMuZ2woKQpvPXAuYQpuPUguUUkoW10scSkKZm9yKHA9Si5JVChwLmIpO3Au
+RigpOyl7bT1wLmdsKCkKbi5wdXNoKFAuRUYoWyJsaW5lIixtLmEsImV4cGxhbmF0aW9uIixtLmIsIm9m
+ZnNldCIsbS5jXSxrLHIpKX1qLlk1KDAsbyxuKX1yZXR1cm4gUC5FRihbInJlZ2lvbnMiLGwuYSwibmF2
+aWdhdGlvbkNvbnRlbnQiLGwuYiwic291cmNlQ29kZSIsbC5jLCJlZGl0cyIsal0sayxyKX19ClQubVEu
+cHJvdG90eXBlPXt9CkwuZS5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXt2YXIgcyxyLHEscCxvLG4s
+bQp0LkIuYShhKQpzPXQuRgpyPXMuYSh3aW5kb3cubG9jYXRpb24pLnBhdGhuYW1lCnE9TC5HNihzLmEo
+d2luZG93LmxvY2F0aW9uKS5ocmVmKQpwPUwuYUsocy5hKHdpbmRvdy5sb2NhdGlvbikuaHJlZikKTC5H
+ZSgpCmlmKHIhPT0iLyIpe3M9ZG9jdW1lbnQucXVlcnlTZWxlY3RvcigiLnJvb3QiKS50ZXh0Q29udGVu
+dApzLnRvU3RyaW5nCnM9ciE9PUMueEIuYlMocyl9ZWxzZSBzPSExCmlmKHMpe3IudG9TdHJpbmcKTC5H
+NyhyLHEscCwhMCxuZXcgTC5WVyhyLHEscCkpfXM9ZG9jdW1lbnQKbz1zLnF1ZXJ5U2VsZWN0b3IoIi5h
+cHBseS1taWdyYXRpb24iKQpvLnRvU3RyaW5nCm89Si5xRihvKQpuPW8uJHRpCm09bi5DKCJ+KDEpPyIp
+LmEobmV3IEwub1ooKSkKdC5aLmEobnVsbCkKVy5KRShvLmEsby5iLG0sITEsbi5jKQpuPXMucXVlcnlT
+ZWxlY3RvcigiLnJlcnVuLW1pZ3JhdGlvbiIpCm4udG9TdHJpbmcKbj1KLnFGKG4pCm09bi4kdGkKVy5K
+RShuLmEsbi5iLG0uQygifigxKT8iKS5hKG5ldyBMLnk4KCkpLCExLG0uYykKbT1zLnF1ZXJ5U2VsZWN0
+b3IoIi5yZXBvcnQtcHJvYmxlbSIpCm0udG9TdHJpbmcKbT1KLnFGKG0pCm49bS4kdGkKVy5KRShtLmEs
+bS5iLG4uQygifigxKT8iKS5hKG5ldyBMLkhpKCkpLCExLG4uYykKcz1zLnF1ZXJ5U2VsZWN0b3IoIi5w
+b3B1cC1wYW5lIC5jbG9zZSIpCnMudG9TdHJpbmcKcz1KLnFGKHMpCm49cy4kdGkKVy5KRShzLmEscy5i
+LG4uQygifigxKT8iKS5hKG5ldyBMLkJUKCkpLCExLG4uYykKbj0kLmMwKCkKbi50b1N0cmluZwpuPUou
+cUYobikKcz1uLiR0aQpXLkpFKG4uYSxuLmIscy5DKCJ+KDEpPyIpLmEobmV3IEwuUFkoKSksITEscy5j
+KX0sCiRTOjE3fQpMLlZXLnByb3RvdHlwZT17CiQwOmZ1bmN0aW9uKCl7TC5Gcih0aGlzLmEsdGhpcy5i
+LHRoaXMuYyl9LAokUzowfQpMLm9aLnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3ZhciBzLHIscSxw
+LG8KdC5WLmEoYSkKaWYoQy5vbC51cyh3aW5kb3csIlRoaXMgd2lsbCBhcHBseSB0aGUgY2hhbmdlcyB5
+b3UndmUgcHJldmlld2VkIHRvIHlvdXIgd29ya2luZyBkaXJlY3RvcnkuIEl0IGlzIHJlY29tbWVuZGVk
+IHlvdSBjb21taXQgYW55IGNoYW5nZXMgeW91IG1hZGUgYmVmb3JlIGRvaW5nIHRoaXMuIikpe3M9SC5R
+SShbXSx0LmQpCmZvcihyPSQuSVIscT1yLmxlbmd0aCxwPTA7cDxyLmxlbmd0aDtyLmxlbmd0aD09PXF8
+fCgwLEgubGspKHIpLCsrcClzLnB1c2gocltwXS5MdCgpKQpzPUwudHkoIi9hcHBseS1taWdyYXRpb24i
+LFAuRUYoWyJuYXZpZ2F0aW9uVHJlZSIsc10sdC5OLHQueCkpLlc3KG5ldyBMLmpyKCksdC5QKQpvPW5l
+dyBMLnFsKCkKdC5iNy5hKG51bGwpCnI9cy4kdGkKcT0kLlgzCmlmKHEhPT1DLk5VKW89UC5WSChvLHEp
+CnMueGYobmV3IFAuRmUobmV3IFAudnMocSxyKSwyLG51bGwsbyxyLkMoIkA8MT4iKS5LcShyLmMpLkMo
+IkZlPDEsMj4iKSkpfX0sCiRTOjF9CkwuanIucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7dmFyIHMK
+dC5mbi5hKGEpCnM9ZG9jdW1lbnQuYm9keQpzLmNsYXNzTGlzdC5yZW1vdmUoInByb3Bvc2VkIikKcy5j
+bGFzc0xpc3QuYWRkKCJhcHBsaWVkIil9LAokUzo0Mn0KTC5xbC5wcm90b3R5cGU9ewokMjpmdW5jdGlv
+bihhLGIpe0wuQzIoIkNvdWxkbid0IGFwcGx5IG1pZ3JhdGlvbiIsdC5LLmEoYSksYil9LAokQzoiJDIi
+LAokUjoyLAokUzo0M30KTC55OC5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy54
+bih0LlYuYShhKSl9LAp4bjpmdW5jdGlvbihhKXt2YXIgcz0wLHI9UC5GWCh0LkgpLHE9MSxwLG89W10s
+bixtLGwsayxqLGkKdmFyICRhc3luYyQkMT1QLmx6KGZ1bmN0aW9uKGIsYyl7aWYoYj09PTEpe3A9Ywpz
+PXF9d2hpbGUodHJ1ZSlzd2l0Y2gocyl7Y2FzZSAwOnE9Mwpkb2N1bWVudC5ib2R5LmNsYXNzTGlzdC5h
+ZGQoInJlcnVubmluZyIpCnM9NgpyZXR1cm4gUC5qUShMLnR5KCIvcmVydW4tbWlncmF0aW9uIixudWxs
+KSwkYXN5bmMkJDEpCmNhc2UgNjpuPWMKaz1uCmsudG9TdHJpbmcKaWYoSC5wOChKLng5KGssInN1Y2Nl
+c3MiKSkpdC5GLmEod2luZG93LmxvY2F0aW9uKS5yZWxvYWQoKQplbHNlIEwuSzAodC5ldy5hKEoueDko
+biwiZXJyb3JzIikpKQpvLnB1c2goNSkKcz00CmJyZWFrCmNhc2UgMzpxPTIKaT1wCm09SC5SdShpKQps
+PUgudHMoaSkKTC5DMigiRmFpbGVkIHRvIHJlcnVuIG1pZ3JhdGlvbiIsbSxsKQpvLnB1c2goNSkKcz00
+CmJyZWFrCmNhc2UgMjpvPVsxXQpjYXNlIDQ6cT0xCmRvY3VtZW50LmJvZHkuY2xhc3NMaXN0LnJlbW92
+ZSgicmVydW5uaW5nIikKcz1vLnBvcCgpCmJyZWFrCmNhc2UgNTpyZXR1cm4gUC55QyhudWxsLHIpCmNh
+c2UgMTpyZXR1cm4gUC5mMyhwLHIpfX0pCnJldHVybiBQLkRJKCRhc3luYyQkMSxyKX0sCiRTOjd9Ckwu
+SGkucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7dC5WLmEoYSkKQy5vbC5Qbyh3aW5kb3csUC5YZCgi
+aHR0cHMiLCJnaXRodWIuY29tIiwiZGFydC1sYW5nL3Nkay9pc3N1ZXMvbmV3IixQLkVGKFsidGl0bGUi
+LCJDdXN0b21lci1yZXBvcnRlZCBpc3N1ZSB3aXRoIE5OQkQgbWlncmF0aW9uIHRvb2wiLCJsYWJlbHMi
+LHUuZCwiYm9keSIsIiMjIyMgU3RlcHMgdG8gcmVwcm9kdWNlXG5cbiMjIyMgV2hhdCBkaWQgeW91IGV4
+cGVjdCB0byBoYXBwZW4/XG5cbiMjIyMgV2hhdCBhY3R1YWxseSBoYXBwZW5lZD9cblxuX1NjcmVlbnNo
+b3RzIGFyZSBhcHByZWNpYXRlZF9cblxuKipEYXJ0IFNESyB2ZXJzaW9uKio6ICIrSC5Faihkb2N1bWVu
+dC5nZXRFbGVtZW50QnlJZCgic2RrLXZlcnNpb24iKS50ZXh0Q29udGVudCkrIlxuXG5UaGFua3MgZm9y
+IGZpbGluZyFcbiJdLHQuTix0LnopKS5nbkQoKSwicmVwb3J0LXByb2JsZW0iKX0sCiRTOjF9CkwuQlQu
+cHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7dmFyIHMKdC5WLmEoYSkKcz1kb2N1bWVudC5xdWVyeVNl
+bGVjdG9yKCIucG9wdXAtcGFuZSIpLnN0eWxlCnMuZGlzcGxheT0ibm9uZSIKcmV0dXJuIm5vbmUifSwK
+JFM6MX0KTC5QWS5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXt2YXIgcyxyLHEscCxvCnQuVi5hKGEp
+CnM9JC5EOSgpLmlubmVyVGV4dApyPXQuaC5hKGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJy5uYXYtcGFu
+ZWwgW2RhdGEtbmFtZSo9IicrVy5MaihzKSsnIl0nKS5wYXJlbnROb2RlKQpxPXIucXVlcnlTZWxlY3Rv
+cigiLnN0YXR1cy1pY29uIikKcD0kLklSCnAudG9TdHJpbmcKbz1MLnl3KHAscykKaWYobyBpbnN0YW5j
+ZW9mIEwuY0Qpe3A9by54CnAudG9TdHJpbmd9ZWxzZSBwPSExCmlmKHApe0wuT3QobykKTC54bihxLG8p
+CkwuQVIocixvKX19LAokUzoxfQpMLkwucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7dmFyIHMscixx
+LHAKdC5CLmEoYSkKcz10LkYKcj1zLmEod2luZG93LmxvY2F0aW9uKS5wYXRobmFtZQpyLnRvU3RyaW5n
+CnE9TC5HNihzLmEod2luZG93LmxvY2F0aW9uKS5ocmVmKQpwPUwuYUsocy5hKHdpbmRvdy5sb2NhdGlv
+bikuaHJlZikKaWYoci5sZW5ndGg+MSlMLkc3KHIscSxwLCExLG51bGwpCmVsc2V7TC5CRShyLEIud1Io
+KSwhMCkKTC5CWCgiJm5ic3A7IixudWxsKX19LAokUzoxN30KTC5XeC5wcm90b3R5cGU9ewokMTpmdW5j
+dGlvbihhKXt2YXIgcyxyLHEscCxvPSJjb2xsYXBzZWQiCnQuVi5hKGEpCnM9dGhpcy5hCnI9Si5ZRShz
+KQpxPXRoaXMuYgpwPUouWUUocSkKaWYoIXIuZ1AocykudGcoMCxvKSl7ci5nUChzKS5pKDAsbykKcC5n
+UChxKS5pKDAsbyl9ZWxzZXtyLmdQKHMpLlIoMCxvKQpwLmdQKHEpLlIoMCxvKX19LAokUzoxfQpMLkFP
+LnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3ZhciBzPUoucUYodC5oLmEoYSkpLHI9cy4kdGkscT1y
+LkMoIn4oMSk/IikuYShuZXcgTC5kTih0aGlzLmEpKQp0LlouYShudWxsKQpXLkpFKHMuYSxzLmIscSwh
+MSxyLmMpfSwKJFM6M30KTC5kTi5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXtyZXR1cm4gTC50Mih0
+LlYuYShhKSx0aGlzLmEpfSwKJFM6MX0KTC5Iby5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXt2YXIg
+cyxyLHEKdC5oLmEoYSkKcz1KLnFGKGEpCnI9cy4kdGkKcT1yLkMoIn4oMSk/IikuYShuZXcgTC54eihh
+LHRoaXMuYSkpCnQuWi5hKG51bGwpClcuSkUocy5hLHMuYixxLCExLHIuYyl9LAokUzozfQpMLnh6LnBy
+b3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3ZhciBzLHIscSxwCnQuVi5hKGEpCnM9dGhpcy5hCnI9cy5n
+ZXRBdHRyaWJ1dGUoImRhdGEtIituZXcgVy5TeShuZXcgVy5pNyhzKSkuT1UoIm9mZnNldCIpKQpyLnRv
+U3RyaW5nCnE9UC5RQShyLG51bGwpCnM9cy5nZXRBdHRyaWJ1dGUoImRhdGEtIituZXcgVy5TeShuZXcg
+Vy5pNyhzKSkuT1UoImxpbmUiKSkKcy50b1N0cmluZwpwPVAuUUEocyxudWxsKQpzPXRoaXMuYgpzLnRv
+U3RyaW5nCkwuaFgocyxxLHApfSwKJFM6MX0KTC5JQy5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXt2
+YXIgcz1KLnFGKHQuaC5hKGEpKSxyPXMuJHRpCnIuQygifigxKT8iKS5hKEwuaVMoKSkKdC5aLmEobnVs
+bCkKVy5KRShzLmEscy5iLEwuaVMoKSwhMSxyLmMpfSwKJFM6M30KTC5mQy5wcm90b3R5cGU9ewokMTpm
+dW5jdGlvbihhKXt0LnAuYShhKQp0aGlzLmEuYU0oMCx0aGlzLmIpfSwKJFM6NDZ9CkwuVG0ucHJvdG90
+eXBlPXsKJDE6ZnVuY3Rpb24oYSl7SC5uKGEpCnJldHVybiBhLmxlbmd0aD40MD9DLnhCLk5qKGEsMCw0
+MCkrIi4uLiI6YX0sCiRTOjJ9CkwublQucHJvdG90eXBlPXsKJDA6ZnVuY3Rpb24oKXtMLkZyKHRoaXMu
+YSx0aGlzLmIsdGhpcy5jKX0sCiRTOjB9CkwuTlkucHJvdG90eXBlPXsKJDA6ZnVuY3Rpb24oKXtMLkZy
+KHRoaXMuYSxudWxsLG51bGwpfSwKJFM6MH0KTC51ZS5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXt0
+LmYuYShhKQpyZXR1cm4gSC5FaihhLnEoMCwic2V2ZXJpdHkiKSkrIiAtICIrSC5FaihhLnEoMCwibWVz
+c2FnZSIpKSsiIGF0ICIrSC5FaihhLnEoMCwibG9jYXRpb24iKSkrIiAtICgiK0guRWooYS5xKDAsImNv
+ZGUiKSkrIikifSwKJFM6NDd9CkwuR0gucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7dC5oLmEoYSkK
+JC56QigpCnQuZXMuYSgkLm93KCkucSgwLCJobGpzIikpLlY3KCJoaWdobGlnaHRCbG9jayIsW2FdKX0s
+CiRTOjN9CkwuRUUucHJvdG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7dmFyIHMscgp0LlYuYShhKS5wcmV2
+ZW50RGVmYXVsdCgpCnM9dGhpcy5hCnI9dGhpcy5iCkwuYWYodC5GLmEod2luZG93LmxvY2F0aW9uKS5w
+YXRobmFtZSxzLHIsITAsbmV3IEwuUUwocyxyKSkKTC5oWCh0aGlzLmMscyxyKX0sCiRTOjF9CkwuUUwu
+cHJvdG90eXBlPXsKJDA6ZnVuY3Rpb24oKXtMLkZyKHQuRi5hKHdpbmRvdy5sb2NhdGlvbikucGF0aG5h
+bWUsdGhpcy5hLHRoaXMuYil9LAokUzowfQpMLlZTLnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3Zh
+ciBzLHI9InNlbGVjdGVkLWZpbGUiCnQuaC5hKGEpCnM9Si5ZRShhKQppZihhLmdldEF0dHJpYnV0ZSgi
+ZGF0YS0iK25ldyBXLlN5KG5ldyBXLmk3KGEpKS5PVSgibmFtZSIpKT09PXRoaXMuYS5hKXMuZ1AoYSku
+aSgwLHIpCmVsc2Ugcy5nUChhKS5SKDAscil9LAokUzozfQpMLlRELnByb3RvdHlwZT17CiQxOmZ1bmN0
+aW9uKGEpe3ZhciBzLHIKdC5WLmEoYSkKcz10aGlzLmEKc3dpdGNoKHMuZ0woKSl7Y2FzZSBDLmN3OmJy
+ZWFrCmNhc2UgQy5XRDpzLm5HKCkKYnJlYWsKY2FzZSBDLlhqOnMuYzIoKQpicmVhawpjYXNlIEMuZGM6
+cy5jMigpCmJyZWFrfXI9dGhpcy5iCkwuYkwocixzKQpMLnhuKHRoaXMuYyxzKQpMLkFSKHIscyl9LAok
+UzoxfQpMLklmLnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe3ZhciBzCnQuVi5hKGEpCnM9dGhpcy5h
+CkwuT3QocykKTC54bih0aGlzLmIscykKTC5BUih0aGlzLmMscyl9LAokUzoxfQpMLnRCLnByb3RvdHlw
+ZT17CiQxOmZ1bmN0aW9uKGEpe3JldHVybiBMLnQyKHQuVi5hKGEpLCEwKX0sCiRTOjF9CkwubTIucHJv
+dG90eXBlPXsKJDE6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMuUkkodC5WLmEoYSkpfSwKUkk6ZnVuY3Rp
+b24oYSl7dmFyIHM9MCxyPVAuRlgodC5IKSxxPTEscCxvPVtdLG49dGhpcyxtLGwsayxqLGksaCxnLGYs
+ZQp2YXIgJGFzeW5jJCQxPVAubHooZnVuY3Rpb24oYixjKXtpZihiPT09MSl7cD1jCnM9cX13aGlsZSh0
+cnVlKXN3aXRjaChzKXtjYXNlIDA6cT0zCmk9ZG9jdW1lbnQKbT1DLkNELnpRKGkucXVlcnlTZWxlY3Rv
+cigiLmNvbnRlbnQiKS5zY3JvbGxUb3ApCmg9dC5OCnM9NgpyZXR1cm4gUC5qUShMLnR5KEwuUTQoIi9h
+cHBseS1oaW50IixQLkZsKGgsaCkpLG4uYS5MdCgpKSwkYXN5bmMkJDEpCmNhc2UgNjpoPW4uYgpnPWgu
+YQpnLnRvU3RyaW5nCmw9TC5VcyhnKQpzPTcKcmV0dXJuIFAualEoTC5HNyhsLG51bGwsaC5iLCExLG51
+bGwpLCRhc3luYyQkMSkKY2FzZSA3OmkuYm9keS5jbGFzc0xpc3QuYWRkKCJuZWVkcy1yZXJ1biIpCmk9
+aS5xdWVyeVNlbGVjdG9yKCIuY29udGVudCIpCmkudG9TdHJpbmcKaS5zY3JvbGxUb3A9Qy5qbi56USht
+KQpxPTEKcz01CmJyZWFrCmNhc2UgMzpxPTIKZT1wCms9SC5SdShlKQpqPUgudHMoZSkKTC5DMigiY291
+bGRuJ3QgYXBwbHkgaGludCIsayxqKQpzPTUKYnJlYWsKY2FzZSAyOnM9MQpicmVhawpjYXNlIDU6cmV0
+dXJuIFAueUMobnVsbCxyKQpjYXNlIDE6cmV0dXJuIFAuZjMocCxyKX19KQpyZXR1cm4gUC5ESSgkYXN5
+bmMkJDEscil9LAokUzo3fQpMLlFXLnByb3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7cmV0dXJuIHRoaXMu
+YSsiOlxuIit0aGlzLmJ9LAokaVJ6OjF9CkwuWEEucHJvdG90eXBlPXsKRWI6ZnVuY3Rpb24oYSxiLGMp
+e3JldHVybiEwfSwKaTA6ZnVuY3Rpb24oYSl7cmV0dXJuITB9LAokaWtGOjF9CkwudnQucHJvdG90eXBl
+PXsKZ0w6ZnVuY3Rpb24oKXt2YXIgcyxyLHEscCxvLG4sbSxsPXRoaXMuZAppZihsLmxlbmd0aD09PTAp
+cmV0dXJuIEMuY3cKcz1DLk5tLmd0SChsKS5nTCgpCmZvcihyPWwubGVuZ3RoLHE9ITAscD0hMCxvPTA7
+bzxsLmxlbmd0aDtsLmxlbmd0aD09PXJ8fCgwLEgubGspKGwpLCsrbyl7bj1sW29dLmdMKCkKaWYobiE9
+cylzPW51bGwKbT1uIT09Qy5jdwppZihtJiZuIT09Qy5XRClxPSExCmlmKG0mJm4hPT1DLlhqKXA9ITF9
+aWYocyE9bnVsbClyZXR1cm4gcwppZihxKXJldHVybiBDLldECmlmKHApcmV0dXJuIEMuWGoKcmV0dXJu
+IEMuZGN9LApMVjpmdW5jdGlvbigpe3ZhciBzLHIscSxwPXRoaXMuZAppZihwIT1udWxsKWZvcihzPXAu
+bGVuZ3RoLHI9MDtyPHM7KytyKXtxPXBbcl0KaWYocS5iPT09JClxLmI9dGhpcwplbHNlIEgudihuZXcg
+SC5jKCJGaWVsZCAncGFyZW50JyBoYXMgYWxyZWFkeSBiZWVuIGluaXRpYWxpemVkLiIpKX19LApjMjpm
+dW5jdGlvbigpe3ZhciBzLHIscSxwLG8KZm9yKHM9dGhpcy5kLHI9cy5sZW5ndGgscT0wO3E8cy5sZW5n
+dGg7cy5sZW5ndGg9PT1yfHwoMCxILmxrKShzKSwrK3Epe3A9c1txXQppZihwIGluc3RhbmNlb2YgTC52
+dClwLmMyKCkKZWxzZXtpZihwIGluc3RhbmNlb2YgTC5jRCl7bz1wLngKby50b1N0cmluZwpvPW8mJnAu
+cj09PUMuWGp9ZWxzZSBvPSExCmlmKG8pcC5yPUMuV0R9fX0sCm5HOmZ1bmN0aW9uKCl7dmFyIHMscixx
+LHAsbwpmb3Iocz10aGlzLmQscj1zLmxlbmd0aCxxPTA7cTxzLmxlbmd0aDtzLmxlbmd0aD09PXJ8fCgw
+LEgubGspKHMpLCsrcSl7cD1zW3FdCmlmKHAgaW5zdGFuY2VvZiBMLnZ0KXAubkcoKQplbHNle2lmKHAg
+aW5zdGFuY2VvZiBMLmNEKXtvPXAueApvLnRvU3RyaW5nCm89byYmcC5yPT09Qy5XRH1lbHNlIG89ITEK
+aWYobylwLnI9Qy5Yan19fSwKTHQ6ZnVuY3Rpb24oKXt2YXIgcyxyPVAuRmwodC5OLHQuWCkKci5ZNSgw
+LCJ0eXBlIiwiZGlyZWN0b3J5IikKci5ZNSgwLCJuYW1lIix0aGlzLmEpCnM9dGhpcy5kCnMudG9TdHJp
+bmcKci5ZNSgwLCJzdWJ0cmVlIixMLlZEKHMpKQpzPXRoaXMuYwppZihzIT1udWxsKXIuWTUoMCwicGF0
+aCIscykKcmV0dXJuIHJ9fQpMLmNELnByb3RvdHlwZT17Ckx0OmZ1bmN0aW9uKCl7dmFyIHMscj10aGlz
+LHE9UC5GbCh0Lk4sdC5YKQpxLlk1KDAsInR5cGUiLCJmaWxlIikKcS5ZNSgwLCJuYW1lIixyLmEpCnM9
+ci5jCmlmKHMhPW51bGwpcS5ZNSgwLCJwYXRoIixzKQpzPXIuZAppZihzIT1udWxsKXEuWTUoMCwiaHJl
+ZiIscykKcz1yLmUKaWYocyE9bnVsbClxLlk1KDAsImVkaXRDb3VudCIscykKcz1yLmYKaWYocyE9bnVs
+bClxLlk1KDAsIndhc0V4cGxpY2l0bHlPcHRlZE91dCIscykKcz1yLnIKaWYocyE9bnVsbClxLlk1KDAs
+Im1pZ3JhdGlvblN0YXR1cyIscy5hKQpzPXIueAppZihzIT1udWxsKXEuWTUoMCwibWlncmF0aW9uU3Rh
+dHVzQ2FuQmVDaGFuZ2VkIixzKQpyZXR1cm4gcX0sCmdMOmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMucn19
+CkwuRDgucHJvdG90eXBlPXt9CkwuTzkucHJvdG90eXBlPXsKdzpmdW5jdGlvbihhKXtyZXR1cm4gdGhp
+cy5ifX0KTC5HYi5wcm90b3R5cGU9ewp3OmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmJ9fQpSLkxMLnBy
+b3RvdHlwZT17Ckx0OmZ1bmN0aW9uKCl7cmV0dXJuIFAuRUYoWyJub2RlSWQiLHRoaXMuYiwia2luZCIs
+dGhpcy5hLmFdLHQuTix0LlgpfX0KUi5NRC5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXtyZXR1cm4g
+dC5ncC5hKGEpLmE9PT10aGlzLmEucSgwLCJraW5kIil9LAokUzo0OH0KUi5INy5wcm90b3R5cGU9ewp3
+OmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmJ9fQpNLmxJLnByb3RvdHlwZT17CldPOmZ1bmN0aW9uKGEs
+Yil7dmFyIHMscixxPXQuZDQKTS5ZRigiYWJzb2x1dGUiLEguUUkoW2IsbnVsbCxudWxsLG51bGwsbnVs
+bCxudWxsLG51bGxdLHEpKQpzPXRoaXMuYQpzPXMuWXIoYik+MCYmIXMuaEsoYikKaWYocylyZXR1cm4g
+YgpzPUQuYWIoKQpyPUguUUkoW3MsYixudWxsLG51bGwsbnVsbCxudWxsLG51bGwsbnVsbF0scSkKTS5Z
+Rigiam9pbiIscikKcmV0dXJuIHRoaXMuSVAobmV3IEgudTYocix0LmVKKSl9LAp6ZjpmdW5jdGlvbihh
+KXt2YXIgcyxyLHE9WC5DTChhLHRoaXMuYSkKcS5JeCgpCnM9cS5kCnI9cy5sZW5ndGgKaWYocj09PTAp
+e3M9cS5iCnJldHVybiBzPT1udWxsPyIuIjpzfWlmKHI9PT0xKXtzPXEuYgpyZXR1cm4gcz09bnVsbD8i
+LiI6c31pZigwPj1yKXJldHVybiBILk9IKHMsLTEpCnMucG9wKCkKcz1xLmUKaWYoMD49cy5sZW5ndGgp
+cmV0dXJuIEguT0gocywtMSkKcy5wb3AoKQpxLkl4KCkKcmV0dXJuIHEudygwKX0sCklQOmZ1bmN0aW9u
+KGEpe3ZhciBzLHIscSxwLG8sbixtLGwsayxqCnQuTy5hKGEpCmZvcihzPWEuJHRpLHI9cy5DKCJhMihj
+WC5FKSIpLmEobmV3IE0ucTcoKSkscT1hLmdtKGEpLHM9bmV3IEguU08ocSxyLHMuQygiU088Y1guRT4i
+KSkscj10aGlzLmEscD0hMSxvPSExLG49IiI7cy5GKCk7KXttPXEuZ2woKQppZihyLmhLKG0pJiZvKXts
+PVguQ0wobSxyKQprPW4uY2hhckNvZGVBdCgwKT09MD9uOm4Kbj1DLnhCLk5qKGssMCxyLlNwKGssITAp
+KQpsLmI9bgppZihyLmRzKG4pKUMuTm0uWTUobC5lLDAsci5nbUkoKSkKbj0iIitsLncoMCl9ZWxzZSBp
+ZihyLllyKG0pPjApe289IXIuaEsobSkKbj0iIittfWVsc2V7aj1tLmxlbmd0aAppZihqIT09MCl7aWYo
+MD49ailyZXR1cm4gSC5PSChtLDApCmo9ci5VZChtWzBdKX1lbHNlIGo9ITEKaWYoIWopaWYocCluKz1y
+LmdtSSgpCm4rPW19cD1yLmRzKG0pfXJldHVybiBuLmNoYXJDb2RlQXQoMCk9PTA/bjpufSwKbzU6ZnVu
+Y3Rpb24oYSl7dmFyIHMKaWYoIXRoaXMueTMoYSkpcmV0dXJuIGEKcz1YLkNMKGEsdGhpcy5hKQpzLnJS
+KCkKcmV0dXJuIHMudygwKX0sCnkzOmZ1bmN0aW9uKGEpe3ZhciBzLHIscSxwLG8sbixtLGwsaz10aGlz
+LmEsaj1rLllyKGEpCmlmKGohPT0wKXtpZihrPT09JC5LaygpKWZvcihzPTA7czxqOysrcylpZihDLnhC
+LlcoYSxzKT09PTQ3KXJldHVybiEwCnI9agpxPTQ3fWVsc2V7cj0wCnE9bnVsbH1mb3IocD1uZXcgSC5x
+aihhKS5hLG89cC5sZW5ndGgscz1yLG49bnVsbDtzPG87KytzLG49cSxxPW0pe209Qy54Qi5PKHAscykK
+aWYoay5yNChtKSl7aWYoaz09PSQuS2soKSYmbT09PTQ3KXJldHVybiEwCmlmKHEhPW51bGwmJmsucjQo
+cSkpcmV0dXJuITAKaWYocT09PTQ2KWw9bj09bnVsbHx8bj09PTQ2fHxrLnI0KG4pCmVsc2UgbD0hMQpp
+ZihsKXJldHVybiEwfX1pZihxPT1udWxsKXJldHVybiEwCmlmKGsucjQocSkpcmV0dXJuITAKaWYocT09
+PTQ2KWs9bj09bnVsbHx8ay5yNChuKXx8bj09PTQ2CmVsc2Ugaz0hMQppZihrKXJldHVybiEwCnJldHVy
+biExfSwKSFA6ZnVuY3Rpb24oYSxiKXt2YXIgcyxyLHEscCxvLG4sbSxsPXRoaXMsaz0nVW5hYmxlIHRv
+IGZpbmQgYSBwYXRoIHRvICInCmI9bC5XTygwLGIpCnM9bC5hCmlmKHMuWXIoYik8PTAmJnMuWXIoYSk+
+MClyZXR1cm4gbC5vNShhKQppZihzLllyKGEpPD0wfHxzLmhLKGEpKWE9bC5XTygwLGEpCmlmKHMuWXIo
+YSk8PTAmJnMuWXIoYik+MCl0aHJvdyBILmIoWC5JNyhrK2ErJyIgZnJvbSAiJytiKyciLicpKQpyPVgu
+Q0woYixzKQpyLnJSKCkKcT1YLkNMKGEscykKcS5yUigpCnA9ci5kCm89cC5sZW5ndGgKaWYobyE9PTAp
+e2lmKDA+PW8pcmV0dXJuIEguT0gocCwwKQpwPUouUk0ocFswXSwiLiIpfWVsc2UgcD0hMQppZihwKXJl
+dHVybiBxLncoMCkKcD1yLmIKbz1xLmIKaWYocCE9bylwPXA9PW51bGx8fG89PW51bGx8fCFzLk5jKHAs
+bykKZWxzZSBwPSExCmlmKHApcmV0dXJuIHEudygwKQp3aGlsZSghMCl7cD1yLmQKbz1wLmxlbmd0aApp
+ZihvIT09MCl7bj1xLmQKbT1uLmxlbmd0aAppZihtIT09MCl7aWYoMD49bylyZXR1cm4gSC5PSChwLDAp
+CnA9cFswXQppZigwPj1tKXJldHVybiBILk9IKG4sMCkKbj1zLk5jKHAsblswXSkKcD1ufWVsc2UgcD0h
+MX1lbHNlIHA9ITEKaWYoIXApYnJlYWsKQy5ObS5XNChyLmQsMCkKQy5ObS5XNChyLmUsMSkKQy5ObS5X
+NChxLmQsMCkKQy5ObS5XNChxLmUsMSl9cD1yLmQKbz1wLmxlbmd0aAppZihvIT09MCl7aWYoMD49byly
+ZXR1cm4gSC5PSChwLDApCnA9Si5STShwWzBdLCIuLiIpfWVsc2UgcD0hMQppZihwKXRocm93IEguYihY
+Lkk3KGsrYSsnIiBmcm9tICInK2IrJyIuJykpCnA9dC5OCkMuTm0uVUcocS5kLDAsUC5POChyLmQubGVu
+Z3RoLCIuLiIsITEscCkpCkMuTm0uWTUocS5lLDAsIiIpCkMuTm0uVUcocS5lLDEsUC5POChyLmQubGVu
+Z3RoLHMuZ21JKCksITEscCkpCnM9cS5kCnA9cy5sZW5ndGgKaWYocD09PTApcmV0dXJuIi4iCmlmKHA+
+MSYmSi5STShDLk5tLmdyWihzKSwiLiIpKXtzPXEuZAppZigwPj1zLmxlbmd0aClyZXR1cm4gSC5PSChz
+LC0xKQpzLnBvcCgpCnM9cS5lCmlmKDA+PXMubGVuZ3RoKXJldHVybiBILk9IKHMsLTEpCnMucG9wKCkK
+aWYoMD49cy5sZW5ndGgpcmV0dXJuIEguT0gocywtMSkKcy5wb3AoKQpDLk5tLmkocywiIil9cS5iPSIi
+CnEuSXgoKQpyZXR1cm4gcS53KDApfX0KTS5xNy5wcm90b3R5cGU9ewokMTpmdW5jdGlvbihhKXtyZXR1
+cm4gSC5uKGEpIT09IiJ9LAokUzo2fQpNLk5vLnByb3RvdHlwZT17CiQxOmZ1bmN0aW9uKGEpe0guayhh
+KQpyZXR1cm4gYT09bnVsbD8ibnVsbCI6JyInK2ErJyInfSwKJFM6NDl9CkIuZnYucHJvdG90eXBlPXsK
+eFo6ZnVuY3Rpb24oYSl7dmFyIHMscj10aGlzLllyKGEpCmlmKHI+MClyZXR1cm4gQy54Qi5OaihhLDAs
+cikKaWYodGhpcy5oSyhhKSl7aWYoMD49YS5sZW5ndGgpcmV0dXJuIEguT0goYSwwKQpzPWFbMF19ZWxz
+ZSBzPW51bGwKcmV0dXJuIHN9LApOYzpmdW5jdGlvbihhLGIpe3JldHVybiBhPT09Yn19ClguV0QucHJv
+dG90eXBlPXsKSXg6ZnVuY3Rpb24oKXt2YXIgcyxyLHE9dGhpcwp3aGlsZSghMCl7cz1xLmQKaWYoIShz
+Lmxlbmd0aCE9PTAmJkouUk0oQy5ObS5ncloocyksIiIpKSlicmVhawpzPXEuZAppZigwPj1zLmxlbmd0
+aClyZXR1cm4gSC5PSChzLC0xKQpzLnBvcCgpCnM9cS5lCmlmKDA+PXMubGVuZ3RoKXJldHVybiBILk9I
+KHMsLTEpCnMucG9wKCl9cz1xLmUKcj1zLmxlbmd0aAppZihyIT09MClDLk5tLlk1KHMsci0xLCIiKX0s
+CnJSOmZ1bmN0aW9uKCl7dmFyIHMscixxLHAsbyxuLG09dGhpcyxsPUguUUkoW10sdC5zKQpmb3Iocz1t
+LmQscj1zLmxlbmd0aCxxPTAscD0wO3A8cy5sZW5ndGg7cy5sZW5ndGg9PT1yfHwoMCxILmxrKShzKSwr
+K3Ape289c1twXQpuPUouaWEobykKaWYoIShuLkROKG8sIi4iKXx8bi5ETihvLCIiKSkpaWYobi5ETihv
+LCIuLiIpKXtuPWwubGVuZ3RoCmlmKG4hPT0wKXtpZigwPj1uKXJldHVybiBILk9IKGwsLTEpCmwucG9w
+KCl9ZWxzZSArK3F9ZWxzZSBDLk5tLmkobCxvKX1pZihtLmI9PW51bGwpQy5ObS5VRyhsLDAsUC5POChx
+LCIuLiIsITEsdC5OKSkKaWYobC5sZW5ndGg9PT0wJiZtLmI9PW51bGwpQy5ObS5pKGwsIi4iKQptLnNu
+SihsKQpzPW0uYQptLnNQaChQLk84KGwubGVuZ3RoKzEscy5nbUkoKSwhMCx0Lk4pKQpyPW0uYgppZihy
+PT1udWxsfHxsLmxlbmd0aD09PTB8fCFzLmRzKHIpKUMuTm0uWTUobS5lLDAsIiIpCnI9bS5iCmlmKHIh
+PW51bGwmJnM9PT0kLktrKCkpe3IudG9TdHJpbmcKbS5iPUgueXMociwiLyIsIlxcIil9bS5JeCgpfSwK
+dzpmdW5jdGlvbihhKXt2YXIgcyxyLHE9dGhpcyxwPXEuYgpwPXAhPW51bGw/IiIrcDoiIgpmb3Iocz0w
+O3M8cS5kLmxlbmd0aDsrK3Mpe3I9cS5lCmlmKHM+PXIubGVuZ3RoKXJldHVybiBILk9IKHIscykKcj1w
+K0guRWoocltzXSkKcD1xLmQKaWYocz49cC5sZW5ndGgpcmV0dXJuIEguT0gocCxzKQpwPXIrSC5Faihw
+W3NdKX1wKz1ILkVqKEMuTm0uZ3JaKHEuZSkpCnJldHVybiBwLmNoYXJDb2RlQXQoMCk9PTA/cDpwfSwK
+c25KOmZ1bmN0aW9uKGEpe3RoaXMuZD10LmEuYShhKX0sCnNQaDpmdW5jdGlvbihhKXt0aGlzLmU9dC5h
+LmEoYSl9fQpYLmR2LnByb3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7cmV0dXJuIlBhdGhFeGNlcHRpb246
+ICIrdGhpcy5hfSwKJGlSejoxfQpPLnpMLnByb3RvdHlwZT17Cnc6ZnVuY3Rpb24oYSl7cmV0dXJuIHRo
+aXMuZ29jKHRoaXMpfX0KRS5PRi5wcm90b3R5cGU9ewpVZDpmdW5jdGlvbihhKXtyZXR1cm4gQy54Qi50
+ZyhhLCIvIil9LApyNDpmdW5jdGlvbihhKXtyZXR1cm4gYT09PTQ3fSwKZHM6ZnVuY3Rpb24oYSl7dmFy
+IHM9YS5sZW5ndGgKcmV0dXJuIHMhPT0wJiZDLnhCLk8oYSxzLTEpIT09NDd9LApTcDpmdW5jdGlvbihh
+LGIpe2lmKGEubGVuZ3RoIT09MCYmQy54Qi5XKGEsMCk9PT00NylyZXR1cm4gMQpyZXR1cm4gMH0sClly
+OmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLlNwKGEsITEpfSwKaEs6ZnVuY3Rpb24oYSl7cmV0dXJuITF9
+LApnb2M6ZnVuY3Rpb24oKXtyZXR1cm4icG9zaXgifSwKZ21JOmZ1bmN0aW9uKCl7cmV0dXJuIi8ifX0K
+Ri5ydS5wcm90b3R5cGU9ewpVZDpmdW5jdGlvbihhKXtyZXR1cm4gQy54Qi50ZyhhLCIvIil9LApyNDpm
+dW5jdGlvbihhKXtyZXR1cm4gYT09PTQ3fSwKZHM6ZnVuY3Rpb24oYSl7dmFyIHM9YS5sZW5ndGgKaWYo
+cz09PTApcmV0dXJuITEKaWYoQy54Qi5PKGEscy0xKSE9PTQ3KXJldHVybiEwCnJldHVybiBDLnhCLlRj
+KGEsIjovLyIpJiZ0aGlzLllyKGEpPT09c30sClNwOmZ1bmN0aW9uKGEsYil7dmFyIHMscixxLHAsbz1h
+Lmxlbmd0aAppZihvPT09MClyZXR1cm4gMAppZihDLnhCLlcoYSwwKT09PTQ3KXJldHVybiAxCmZvcihz
+PTA7czxvOysrcyl7cj1DLnhCLlcoYSxzKQppZihyPT09NDcpcmV0dXJuIDAKaWYocj09PTU4KXtpZihz
+PT09MClyZXR1cm4gMApxPUMueEIuWFUoYSwiLyIsQy54Qi5RaShhLCIvLyIscysxKT9zKzM6cykKaWYo
+cTw9MClyZXR1cm4gbwppZighYnx8bzxxKzMpcmV0dXJuIHEKaWYoIUMueEIubihhLCJmaWxlOi8vIikp
+cmV0dXJuIHEKaWYoIUIuWXUoYSxxKzEpKXJldHVybiBxCnA9cSszCnJldHVybiBvPT09cD9wOnErNH19
+cmV0dXJuIDB9LApZcjpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5TcChhLCExKX0sCmhLOmZ1bmN0aW9u
+KGEpe3JldHVybiBhLmxlbmd0aCE9PTAmJkMueEIuVyhhLDApPT09NDd9LApnb2M6ZnVuY3Rpb24oKXty
+ZXR1cm4idXJsIn0sCmdtSTpmdW5jdGlvbigpe3JldHVybiIvIn19CkwuSVYucHJvdG90eXBlPXsKVWQ6
+ZnVuY3Rpb24oYSl7cmV0dXJuIEMueEIudGcoYSwiLyIpfSwKcjQ6ZnVuY3Rpb24oYSl7cmV0dXJuIGE9
+PT00N3x8YT09PTkyfSwKZHM6ZnVuY3Rpb24oYSl7dmFyIHM9YS5sZW5ndGgKaWYocz09PTApcmV0dXJu
+ITEKcz1DLnhCLk8oYSxzLTEpCnJldHVybiEocz09PTQ3fHxzPT09OTIpfSwKU3A6ZnVuY3Rpb24oYSxi
+KXt2YXIgcyxyLHE9YS5sZW5ndGgKaWYocT09PTApcmV0dXJuIDAKcz1DLnhCLlcoYSwwKQppZihzPT09
+NDcpcmV0dXJuIDEKaWYocz09PTkyKXtpZihxPDJ8fEMueEIuVyhhLDEpIT09OTIpcmV0dXJuIDEKcj1D
+LnhCLlhVKGEsIlxcIiwyKQppZihyPjApe3I9Qy54Qi5YVShhLCJcXCIscisxKQppZihyPjApcmV0dXJu
+IHJ9cmV0dXJuIHF9aWYocTwzKXJldHVybiAwCmlmKCFCLk9TKHMpKXJldHVybiAwCmlmKEMueEIuVyhh
+LDEpIT09NTgpcmV0dXJuIDAKcT1DLnhCLlcoYSwyKQppZighKHE9PT00N3x8cT09PTkyKSlyZXR1cm4g
+MApyZXR1cm4gM30sCllyOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLlNwKGEsITEpfSwKaEs6ZnVuY3Rp
+b24oYSl7cmV0dXJuIHRoaXMuWXIoYSk9PT0xfSwKT3Q6ZnVuY3Rpb24oYSxiKXt2YXIgcwppZihhPT09
+YilyZXR1cm4hMAppZihhPT09NDcpcmV0dXJuIGI9PT05MgppZihhPT09OTIpcmV0dXJuIGI9PT00Nwpp
+ZigoYV5iKSE9PTMyKXJldHVybiExCnM9YXwzMgpyZXR1cm4gcz49OTcmJnM8PTEyMn0sCk5jOmZ1bmN0
+aW9uKGEsYil7dmFyIHMscgppZihhPT09YilyZXR1cm4hMApzPWEubGVuZ3RoCmlmKHMhPT1iLmxlbmd0
+aClyZXR1cm4hMQpmb3Iocj0wO3I8czsrK3IpaWYoIXRoaXMuT3QoQy54Qi5XKGEsciksQy54Qi5XKGIs
+cikpKXJldHVybiExCnJldHVybiEwfSwKZ29jOmZ1bmN0aW9uKCl7cmV0dXJuIndpbmRvd3MifSwKZ21J
+OmZ1bmN0aW9uKCl7cmV0dXJuIlxcIn19OyhmdW5jdGlvbiBhbGlhc2VzKCl7dmFyIHM9Si5Hdi5wcm90
+b3R5cGUKcy5VPXMudwpzPUouTUYucHJvdG90eXBlCnMudD1zLncKcz1QLmNYLnByb3RvdHlwZQpzLkdH
+PXMuZXYKcz1QLk1oLnByb3RvdHlwZQpzLnhiPXMudwpzPVcuY3YucHJvdG90eXBlCnMuRFc9cy5yNgpz
+PVcubTYucHJvdG90eXBlCnMuakY9cy5FYgpzPVAuRTQucHJvdG90eXBlCnMuVXI9cy5xCnMuZTQ9cy5Z
+NQpzPVAudmcucHJvdG90eXBlCnMuYmg9cy5ZNX0pKCk7KGZ1bmN0aW9uIGluc3RhbGxUZWFyT2Zmcygp
+e3ZhciBzPWh1bmtIZWxwZXJzLl9zdGF0aWNfMSxyPWh1bmtIZWxwZXJzLl9zdGF0aWNfMCxxPWh1bmtI
+ZWxwZXJzLmluc3RhbGxJbnN0YW5jZVRlYXJPZmYscD1odW5rSGVscGVycy5pbnN0YWxsU3RhdGljVGVh
+ck9mZixvPWh1bmtIZWxwZXJzLl9pbnN0YW5jZV8xdQpzKFAsIkVYIiwiWlYiLDUpCnMoUCwieXQiLCJv
+QSIsNSkKcyhQLCJxVyIsIkJ6Iiw1KQpyKFAsIlVJIiwiZU4iLDApCnEoUC5QZi5wcm90b3R5cGUsImdZ
+SiIsMCwxLG51bGwsWyIkMiIsIiQxIl0sWyJ3MCIsInBtIl0sNDAsMCwwKQpzKFAsIkN5IiwiTkMiLDQp
+CnMoUCwiUEgiLCJNdCIsMikKcChXLCJwUyIsNCxudWxsLFsiJDQiXSxbInFEIl0sMTQsMCkKcChXLCJW
+NCIsNCxudWxsLFsiJDQiXSxbIm5aIl0sMTQsMCkKbyhQLkFzLnByb3RvdHlwZSwiZ3VNIiwiViIsMikK
+cyhQLCJpRyIsIndZIiw1MikKcyhQLCJ3MCIsImRVIiwzNSkKcyhMLCJpUyIsImk2IiwxKX0pKCk7KGZ1
+bmN0aW9uIGluaGVyaXRhbmNlKCl7dmFyIHM9aHVua0hlbHBlcnMubWl4aW4scj1odW5rSGVscGVycy5p
+bmhlcml0LHE9aHVua0hlbHBlcnMuaW5oZXJpdE1hbnkKcihQLk1oLG51bGwpCnEoUC5NaCxbSC5GSyxK
+Lkd2LEoubTEsUC5jWCxILmVULFAuWFMsUC5uWSxILmE3LFAuQW4sSC5GdSxILkpCLEguU1UsSC5SZSxI
+Lnd2LFAuUG4sSC5XVSxILkxJLEguVHAsSC5mOSxILnRlLEguYnEsSC5YTyxILmtyLFAuWWssSC52aCxI
+Lk42LEguVlIsSC5FSyxILlBiLEgudFEsSC5TZCxILkpjLEguRyxQLlczLFAuaWgsUC5GeSxQLkdWLFAu
+Q3csUC5QZixQLkZlLFAudnMsUC5PTSxQLnFoLFAuTU8sUC5rVCxQLnhJLFAubTAsUC5wUixQLmJuLFAu
+bG0sUC5sRCxQLktQLFAubGYsUC5XWSxQLlVrLFAuU2gsUC5SdyxQLmJ6LFAuaVAsUC5rNSxQLktZLFAu
+Q0QsUC5hRSxQLk4zLFAuYzgsUC5aZCxQLk0sUC5EbixQLlBFLFAuVWYsVy5pZCxXLkZrLFcuSlEsVy5H
+bSxXLnZELFcubTYsVy5PdyxXLlc5LFcuZFcsVy5tayxXLktvLFAuaUosUC5FNCxVLmQyLFUuU2UsVS5N
+bCxVLnlELFUud2IsQi5qOCxCLnFwLFQubVEsTC5YQSxMLkQ4LEwuTzksTC5HYixSLkxMLFIuSDcsTS5s
+SSxPLnpMLFguV0QsWC5kdl0pCnEoSi5HdixbSi55RSxKLndlLEouTUYsSi5qZCxKLnFJLEouRHIsSC5F
+VCxXLkQwLFcuQXosVy5MZSxXLk5oLFcuYWUsVy5JQixXLm43LFcuZWEsVy5icixXLlNnLFcudTgsVy5L
+NyxXLlhXLFAuaEZdKQpxKEouTUYsW0ouaUMsSi5rZCxKLmM1XSkKcihKLlBvLEouamQpCnEoSi5xSSxb
+Si5iVSxKLmtEXSkKcShQLmNYLFtILkJSLEguYlEsSC5pMSxILlU1LEguQU0sSC51NixILlhSLFAubVcs
+SC51bl0pCnEoSC5CUixbSC5aeSxILlFDXSkKcihILm9sLEguWnkpCnIoSC5VcSxILlFDKQpyKEgualYs
+SC5VcSkKcShQLlhTLFtILmMsUC5FeixILmF6LEgudlYsSC5FcSxQLkM2LEgua1MsUC5VZCxQLkYsUC5B
+VCxQLm1wLFAudWIsUC5kcyxQLmxqLFAuVVYsUC5wLEwuUVddKQpyKFAudXksUC5uWSkKcShQLnV5LFtI
+LncyLFcud3osVy5lN10pCnIoSC5xaixILncyKQpxKEguYlEsW0guYUwsSC5NQixILmk1XSkKcShILmFM
+LFtILm5ILEgubEosUC5pOF0pCnIoSC54eSxILmkxKQpxKFAuQW4sW0guTUgsSC5TTyxILlUxXSkKcihI
+LmQ1LEguQU0pCnIoUC5SVSxQLlBuKQpyKFAuR2osUC5SVSkKcihILlBELFAuR2opCnIoSC5MUCxILldV
+KQpxKEguVHAsW0guQ2osSC5sYyxILmRDLEgud04sSC5WWCxQLnRoLFAuaGEsUC5WcyxQLkZ0LFAueUgs
+UC5XTSxQLlNYLFAuR3MsUC5kYSxQLm9RLFAucFYsUC5VNyxQLnZyLFAucnQsUC5LRixQLlpMLFAuUlQs
+UC5qWixQLnJxLFAuUlcsUC5CNSxQLnVPLFAuRXYsUC5WcCxQLk9SLFAucmEsUC55USxQLnhyLFAuTnos
+UC50aSxQLldGLFAubjEsUC5jUyxQLlZDLFAuSlQsUC5SWixQLk1FLFAueTUsUC55SSxQLmM2LFAucWQs
+Vy5DdixXLktTLFcuQTMsVy52TixXLlV2LFcuRWcsVy5FbyxXLldrLFcuSUEsVy5mbSxQLkUyLFAuamcs
+UC5HRSxQLk43LFAudVEsUC5QQyxQLm10LFAuUVMsUC5ucCxQLlV0LFUuYU4sVS5iMCxMLmUsTC5WVyxM
+Lm9aLEwuanIsTC5xbCxMLnk4LEwuSGksTC5CVCxMLlBZLEwuTCxMLld4LEwuQU8sTC5kTixMLkhvLEwu
+eHosTC5JQyxMLmZDLEwuVG0sTC5uVCxMLk5ZLEwudWUsTC5HSCxMLkVFLEwuUUwsTC5WUyxMLlRELEwu
+SWYsTC50QixMLm0yLFIuTUQsTS5xNyxNLk5vXSkKcihILlcwLFAuRXopCnEoSC5sYyxbSC56eCxILnJU
+XSkKcihILmtZLFAuQzYpCnIoUC5pbCxQLllrKQpxKFAuaWwsW0guTjUsUC51dyxXLmNmLFcuU3ldKQpx
+KFAubVcsW0guS1csUC5xNF0pCnIoSC5MWixILkVUKQpxKEguTFosW0guUkcsSC5XQl0pCnIoSC5WUCxI
+LlJHKQpyKEguRGcsSC5WUCkKcihILlpHLEguV0IpCnIoSC5QZyxILlpHKQpxKEguUGcsW0gueGosSC5k
+RSxILlpBLEguZFQsSC5QcSxILmVFLEguVjZdKQpyKEguaU0sSC5rUykKcihQLlpmLFAuUGYpCnIoUC5K
+aSxQLm0wKQpyKFAuWHYsUC5wUikKcihQLmI2LFAuWHYpCnIoUC5WaixQLldZKQpxKFAuVWssW1AuQ1Ys
+UC5aaSxQLmJ5XSkKcihQLndJLFAua1QpCnEoUC53SSxbUC5VOCxQLm9qLFAuTXgsUC5FMyxQLkdZXSkK
+cihQLks4LFAuVWQpCnIoUC50dSxQLlNoKQpyKFAudTUsUC5aaSkKcShQLkFULFtQLmJKLFAuZVldKQpy
+KFAucWUsUC5EbikKcShXLkQwLFtXLktWLFcud2EsVy5LNSxXLkNtXSkKcShXLktWLFtXLmN2LFcubngs
+Vy5RRixXLkNRXSkKcShXLmN2LFtXLnFFLFAuaGldKQpxKFcucUUsW1cuR2gsVy5mWSxXLnJaLFcuUVAs
+Vy5oNCxXLlNOLFcubHAsVy5UYixXLkl2LFcuV1AsVy55WV0pCnIoVy5vSixXLkxlKQpyKFcuaEgsVy5B
+eikKcihXLlZiLFcuUUYpCnIoVy5mSixXLndhKQpxKFcuZWEsW1cudzYsVy5ld10pCnIoVy5BaixXLnc2
+KQpyKFcuckIsVy5LNykKcihXLkJILFcuckIpCnIoVy53NCxXLklCKQpyKFcub2EsVy5YVykKcihXLnJo
+LFcub2EpCnIoVy5pNyxXLmNmKQpyKFAuQXMsUC5WaikKcShQLkFzLFtXLkk0LFAuS2VdKQpyKFcuUk8s
+UC5xaCkKcihXLkNxLFcuUk8pCnIoVy54QyxQLk1PKQpyKFcuY3QsVy5tNikKcihQLkJmLFAuaUopCnEo
+UC5FNCxbUC5yNyxQLnZnXSkKcihQLlR6LFAudmcpCnIoUC5uZCxQLmhpKQpxKEwuRDgsW0wudnQsTC5j
+RF0pCnIoQi5mdixPLnpMKQpxKEIuZnYsW0UuT0YsRi5ydSxMLklWXSkKcyhILncyLEguUmUpCnMoSC5R
+QyxQLmxEKQpzKEguUkcsUC5sRCkKcyhILlZQLEguU1UpCnMoSC5XQixQLmxEKQpzKEguWkcsSC5TVSkK
+cyhQLm5ZLFAubEQpCnMoUC5XWSxQLmxmKQpzKFAuUlUsUC5LUCkKcyhQLnBSLFAubGYpCnMoVy5MZSxX
+LmlkKQpzKFcuSzcsUC5sRCkKcyhXLnJCLFcuR20pCnMoVy5YVyxQLmxEKQpzKFcub2EsVy5HbSkKcyhQ
+LnZnLFAubEQpfSkoKQp2YXIgdj17dHlwZVVuaXZlcnNlOntlQzpuZXcgTWFwKCksdFI6e30sZVQ6e30s
+dFBWOnt9LHNFQTpbXX0sbWFuZ2xlZEdsb2JhbE5hbWVzOntJajoiaW50IixDUDoiZG91YmxlIixaWjoi
+bnVtIixxVToiU3RyaW5nIixhMjoiYm9vbCIsYzg6Ik51bGwiLHpNOiJMaXN0In0sbWFuZ2xlZE5hbWVz
+Ont9LHR5cGVzOlsifigpIiwifihBaikiLCJxVShxVSkiLCJ+KGN2KSIsIkAoQCkiLCJ+KH4oKSkiLCJh
+MihxVSkiLCJiODx+PihBaikiLCJ+KHFVLHFVKSIsIn4oTWg/LE1oPykiLCJAKCkiLCJ+KHFVLEApIiwi
+fih4dTxxVT4pIiwiYzgoQCkiLCJhMihjdixxVSxxVSxKUSkiLCJjOCgpIiwiYTIoa0YpIiwiYzgoZWEp
+IiwifihuNixxVSxJaikiLCJ+KHFVW0BdKSIsIklqKElqLElqKSIsIkAocVUpIiwifihxVSxxVT8pIiwi
+bjYoQCxAKSIsIn4ocVUsSWopIiwiYTIoS1YpIiwiWjA8cVUscVU+KFowPHFVLHFVPixxVSkiLCJ+KGVh
+KSIsIn4oR0QsQCkiLCJ2czxAPihAKSIsIn4oS1YsS1Y/KSIsIn4oQCxAKSIsImM4KEAsQCkiLCJhMih4
+dTxxVT4pIiwiYzgoTWgsR3opIiwiTWg/KEApIiwiVHo8QD4oQCkiLCJFNChAKSIsIkxMKEApIiwiWjA8
+cVUsTWg/PihMTCkiLCJ+KE1oW0d6P10pIiwiYzgofigpKSIsImM4KFowPHFVLE1oPz4/KSIsImM4KE1o
+LEApIiwicjcoQCkiLCJ+KElqLEApIiwifihldykiLCJxVShaMDxALEA+KSIsImEyKEg3KSIsInFVKHFV
+PykiLCJAKEAscVUpIiwifihAKSIsIk1oPyhNaD8pIiwiYzgoQCxHeikiXSxpbnRlcmNlcHRvcnNCeVRh
+ZzpudWxsLGxlYWZUYWdzOm51bGwsYXJyYXlSdGk6dHlwZW9mIFN5bWJvbD09ImZ1bmN0aW9uIiYmdHlw
+ZW9mIFN5bWJvbCgpPT0ic3ltYm9sIj9TeW1ib2woIiR0aSIpOiIkdGkifQpILnhiKHYudHlwZVVuaXZl
+cnNlLEpTT04ucGFyc2UoJ3siaUMiOiJNRiIsImtkIjoiTUYiLCJjNSI6Ik1GIiwicngiOiJlYSIsImU1
+IjoiZWEiLCJZMCI6ImhpIiwidHAiOiJoaSIsIkc4IjoiZXciLCJNciI6InFFIiwiZUwiOiJxRSIsIkkw
+IjoiS1YiLCJocyI6IktWIiwiWGciOiJRRiIsIm5yIjoiQWoiLCJ5NCI6Inc2IiwiYVAiOiJDbSIsInhj
+IjoibngiLCJrSiI6Im54IiwielUiOiJEZyIsImRmIjoiRVQiLCJ5RSI6eyJhMiI6W119LCJ3ZSI6eyJj
+OCI6W119LCJNRiI6eyJ2bSI6W119LCJqZCI6eyJ6TSI6WyIxIl0sImJRIjpbIjEiXSwiY1giOlsiMSJd
+fSwiUG8iOnsiamQiOlsiMSJdLCJ6TSI6WyIxIl0sImJRIjpbIjEiXSwiY1giOlsiMSJdfSwibTEiOnsi
+QW4iOlsiMSJdfSwicUkiOnsiQ1AiOltdLCJaWiI6W119LCJiVSI6eyJDUCI6W10sIklqIjpbXSwiWloi
+OltdfSwia0QiOnsiQ1AiOltdLCJaWiI6W119LCJEciI6eyJxVSI6W10sInZYIjpbXX0sImJRIjp7ImNY
+IjpbIjEiXX0sIkJSIjp7ImNYIjpbIjIiXX0sImVUIjp7IkFuIjpbIjIiXX0sIlp5Ijp7IkJSIjpbIjEi
+LCIyIl0sImNYIjpbIjIiXSwiY1guRSI6IjIifSwib2wiOnsiWnkiOlsiMSIsIjIiXSwiQlIiOlsiMSIs
+IjIiXSwiYlEiOlsiMiJdLCJjWCI6WyIyIl0sImNYLkUiOiIyIn0sIlVxIjp7ImxEIjpbIjIiXSwiek0i
+OlsiMiJdLCJCUiI6WyIxIiwiMiJdLCJiUSI6WyIyIl0sImNYIjpbIjIiXX0sImpWIjp7IlVxIjpbIjEi
+LCIyIl0sImxEIjpbIjIiXSwiek0iOlsiMiJdLCJCUiI6WyIxIiwiMiJdLCJiUSI6WyIyIl0sImNYIjpb
+IjIiXSwibEQuRSI6IjIiLCJjWC5FIjoiMiJ9LCJjIjp7IlhTIjpbXX0sInFqIjp7ImxEIjpbIklqIl0s
+IlJlIjpbIklqIl0sInpNIjpbIklqIl0sImJRIjpbIklqIl0sImNYIjpbIklqIl0sImxELkUiOiJJaiIs
+IlJlLkUiOiJJaiJ9LCJhTCI6eyJiUSI6WyIxIl0sImNYIjpbIjEiXX0sIm5IIjp7ImFMIjpbIjEiXSwi
+YlEiOlsiMSJdLCJjWCI6WyIxIl0sImFMLkUiOiIxIiwiY1guRSI6IjEifSwiYTciOnsiQW4iOlsiMSJd
+fSwiaTEiOnsiY1giOlsiMiJdLCJjWC5FIjoiMiJ9LCJ4eSI6eyJpMSI6WyIxIiwiMiJdLCJiUSI6WyIy
+Il0sImNYIjpbIjIiXSwiY1guRSI6IjIifSwiTUgiOnsiQW4iOlsiMiJdfSwibEoiOnsiYUwiOlsiMiJd
+LCJiUSI6WyIyIl0sImNYIjpbIjIiXSwiYUwuRSI6IjIiLCJjWC5FIjoiMiJ9LCJVNSI6eyJjWCI6WyIx
+Il0sImNYLkUiOiIxIn0sIlNPIjp7IkFuIjpbIjEiXX0sIkFNIjp7ImNYIjpbIjEiXSwiY1guRSI6IjEi
+fSwiZDUiOnsiQU0iOlsiMSJdLCJiUSI6WyIxIl0sImNYIjpbIjEiXSwiY1guRSI6IjEifSwiVTEiOnsi
+QW4iOlsiMSJdfSwiTUIiOnsiYlEiOlsiMSJdLCJjWCI6WyIxIl0sImNYLkUiOiIxIn0sIkZ1Ijp7IkFu
+IjpbIjEiXX0sInU2Ijp7ImNYIjpbIjEiXSwiY1guRSI6IjEifSwiSkIiOnsiQW4iOlsiMSJdfSwidzIi
+OnsibEQiOlsiMSJdLCJSZSI6WyIxIl0sInpNIjpbIjEiXSwiYlEiOlsiMSJdLCJjWCI6WyIxIl19LCJ3
+diI6eyJHRCI6W119LCJQRCI6eyJHaiI6WyIxIiwiMiJdLCJSVSI6WyIxIiwiMiJdLCJQbiI6WyIxIiwi
+MiJdLCJLUCI6WyIxIiwiMiJdLCJaMCI6WyIxIiwiMiJdfSwiV1UiOnsiWjAiOlsiMSIsIjIiXX0sIkxQ
+Ijp7IldVIjpbIjEiLCIyIl0sIlowIjpbIjEiLCIyIl19LCJYUiI6eyJjWCI6WyIxIl0sImNYLkUiOiIx
+In0sIkxJIjp7InZRIjpbXX0sIlcwIjp7IlhTIjpbXX0sImF6Ijp7IlhTIjpbXX0sInZWIjp7IlhTIjpb
+XX0sInRlIjp7IlJ6IjpbXX0sIlhPIjp7Ikd6IjpbXX0sIlRwIjp7IkVIIjpbXX0sImxjIjp7IkVIIjpb
+XX0sInp4Ijp7IkVIIjpbXX0sInJUIjp7IkVIIjpbXX0sIkVxIjp7IlhTIjpbXX0sImtZIjp7IlhTIjpb
+XX0sIk41Ijp7IllrIjpbIjEiLCIyIl0sIkZvIjpbIjEiLCIyIl0sIlowIjpbIjEiLCIyIl0sIllrLksi
+OiIxIiwiWWsuViI6IjIifSwiaTUiOnsiYlEiOlsiMSJdLCJjWCI6WyIxIl0sImNYLkUiOiIxIn0sIk42
+Ijp7IkFuIjpbIjEiXX0sIlZSIjp7IndMIjpbXSwidlgiOltdfSwiRUsiOnsiaWIiOltdLCJPZCI6W119
+LCJLVyI6eyJjWCI6WyJpYiJdLCJjWC5FIjoiaWIifSwiUGIiOnsiQW4iOlsiaWIiXX0sInRRIjp7Ik9k
+IjpbXX0sInVuIjp7ImNYIjpbIk9kIl0sImNYLkUiOiJPZCJ9LCJTZCI6eyJBbiI6WyJPZCJdfSwiRVQi
+OnsiQVMiOltdfSwiTFoiOnsiWGoiOlsiMSJdLCJFVCI6W10sIkFTIjpbXX0sIkRnIjp7ImxEIjpbIkNQ
+Il0sIlhqIjpbIkNQIl0sInpNIjpbIkNQIl0sIkVUIjpbXSwiYlEiOlsiQ1AiXSwiQVMiOltdLCJjWCI6
+WyJDUCJdLCJTVSI6WyJDUCJdLCJsRC5FIjoiQ1AifSwiUGciOnsibEQiOlsiSWoiXSwiWGoiOlsiSWoi
+XSwiek0iOlsiSWoiXSwiRVQiOltdLCJiUSI6WyJJaiJdLCJBUyI6W10sImNYIjpbIklqIl0sIlNVIjpb
+IklqIl19LCJ4aiI6eyJsRCI6WyJJaiJdLCJYaiI6WyJJaiJdLCJ6TSI6WyJJaiJdLCJFVCI6W10sImJR
+IjpbIklqIl0sIkFTIjpbXSwiY1giOlsiSWoiXSwiU1UiOlsiSWoiXSwibEQuRSI6IklqIn0sImRFIjp7
+ImxEIjpbIklqIl0sIlhqIjpbIklqIl0sInpNIjpbIklqIl0sIkVUIjpbXSwiYlEiOlsiSWoiXSwiQVMi
+OltdLCJjWCI6WyJJaiJdLCJTVSI6WyJJaiJdLCJsRC5FIjoiSWoifSwiWkEiOnsibEQiOlsiSWoiXSwi
+WGoiOlsiSWoiXSwiek0iOlsiSWoiXSwiRVQiOltdLCJiUSI6WyJJaiJdLCJBUyI6W10sImNYIjpbIklq
+Il0sIlNVIjpbIklqIl0sImxELkUiOiJJaiJ9LCJkVCI6eyJsRCI6WyJJaiJdLCJYaiI6WyJJaiJdLCJ6
+TSI6WyJJaiJdLCJFVCI6W10sImJRIjpbIklqIl0sIkFTIjpbXSwiY1giOlsiSWoiXSwiU1UiOlsiSWoi
+XSwibEQuRSI6IklqIn0sIlBxIjp7ImxEIjpbIklqIl0sIlhqIjpbIklqIl0sInpNIjpbIklqIl0sIkVU
+IjpbXSwiYlEiOlsiSWoiXSwiQVMiOltdLCJjWCI6WyJJaiJdLCJTVSI6WyJJaiJdLCJsRC5FIjoiSWoi
+fSwiZUUiOnsibEQiOlsiSWoiXSwiWGoiOlsiSWoiXSwiek0iOlsiSWoiXSwiRVQiOltdLCJiUSI6WyJJ
+aiJdLCJBUyI6W10sImNYIjpbIklqIl0sIlNVIjpbIklqIl0sImxELkUiOiJJaiJ9LCJWNiI6eyJsRCI6
+WyJJaiJdLCJuNiI6W10sIlhqIjpbIklqIl0sInpNIjpbIklqIl0sIkVUIjpbXSwiYlEiOlsiSWoiXSwi
+QVMiOltdLCJjWCI6WyJJaiJdLCJTVSI6WyJJaiJdLCJsRC5FIjoiSWoifSwia1MiOnsiWFMiOltdfSwi
+aU0iOnsiWFMiOltdfSwidnMiOnsiYjgiOlsiMSJdfSwiR1YiOnsiQW4iOlsiMSJdfSwicTQiOnsiY1gi
+OlsiMSJdLCJjWC5FIjoiMSJ9LCJDdyI6eyJYUyI6W119LCJaZiI6eyJQZiI6WyIxIl19LCJtMCI6eyJR
+bSI6W119LCJKaSI6eyJtMCI6W10sIlFtIjpbXX0sImI2Ijp7ImxmIjpbIjEiXSwieHUiOlsiMSJdLCJi
+USI6WyIxIl0sImNYIjpbIjEiXSwibGYuRSI6IjEifSwibG0iOnsiQW4iOlsiMSJdfSwibVciOnsiY1gi
+OlsiMSJdfSwidXkiOnsibEQiOlsiMSJdLCJ6TSI6WyIxIl0sImJRIjpbIjEiXSwiY1giOlsiMSJdfSwi
+aWwiOnsiWWsiOlsiMSIsIjIiXSwiWjAiOlsiMSIsIjIiXX0sIllrIjp7IlowIjpbIjEiLCIyIl19LCJQ
+biI6eyJaMCI6WyIxIiwiMiJdfSwiR2oiOnsiUlUiOlsiMSIsIjIiXSwiUG4iOlsiMSIsIjIiXSwiS1Ai
+OlsiMSIsIjIiXSwiWjAiOlsiMSIsIjIiXX0sIlZqIjp7ImxmIjpbIjEiXSwieHUiOlsiMSJdLCJiUSI6
+WyIxIl0sImNYIjpbIjEiXX0sIlh2Ijp7ImxmIjpbIjEiXSwieHUiOlsiMSJdLCJiUSI6WyIxIl0sImNY
+IjpbIjEiXX0sInV3Ijp7IllrIjpbInFVIiwiQCJdLCJaMCI6WyJxVSIsIkAiXSwiWWsuSyI6InFVIiwi
+WWsuViI6IkAifSwiaTgiOnsiYUwiOlsicVUiXSwiYlEiOlsicVUiXSwiY1giOlsicVUiXSwiYUwuRSI6
+InFVIiwiY1guRSI6InFVIn0sIkNWIjp7IlVrIjpbInpNPElqPiIsInFVIl0sIlVrLlMiOiJ6TTxJaj4i
+fSwiVTgiOnsid0kiOlsiek08SWo+IiwicVUiXX0sIlppIjp7IlVrIjpbInFVIiwiek08SWo+Il19LCJV
+ZCI6eyJYUyI6W119LCJLOCI6eyJYUyI6W119LCJieSI6eyJVayI6WyJNaD8iLCJxVSJdLCJVay5TIjoi
+TWg/In0sIm9qIjp7IndJIjpbIk1oPyIsInFVIl19LCJNeCI6eyJ3SSI6WyJxVSIsIk1oPyJdfSwidTUi
+OnsiVWsiOlsicVUiLCJ6TTxJaj4iXSwiVWsuUyI6InFVIn0sIkUzIjp7IndJIjpbInFVIiwiek08SWo+
+Il19LCJHWSI6eyJ3SSI6WyJ6TTxJaj4iLCJxVSJdfSwiQ1AiOnsiWloiOltdfSwiSWoiOnsiWloiOltd
+fSwiek0iOnsiYlEiOlsiMSJdLCJjWCI6WyIxIl19LCJpYiI6eyJPZCI6W119LCJ4dSI6eyJiUSI6WyIx
+Il0sImNYIjpbIjEiXX0sInFVIjp7InZYIjpbXX0sIkM2Ijp7IlhTIjpbXX0sIkV6Ijp7IlhTIjpbXX0s
+IkYiOnsiWFMiOltdfSwiQVQiOnsiWFMiOltdfSwiYkoiOnsiWFMiOltdfSwiZVkiOnsiWFMiOltdfSwi
+bXAiOnsiWFMiOltdfSwidWIiOnsiWFMiOltdfSwiZHMiOnsiWFMiOltdfSwibGoiOnsiWFMiOltdfSwi
+VVYiOnsiWFMiOltdfSwiazUiOnsiWFMiOltdfSwiS1kiOnsiWFMiOltdfSwicCI6eyJYUyI6W119LCJD
+RCI6eyJSeiI6W119LCJhRSI6eyJSeiI6W119LCJaZCI6eyJHeiI6W119LCJNIjp7IkJMIjpbXX0sIkRu
+Ijp7ImlEIjpbXX0sIlVmIjp7ImlEIjpbXX0sInFlIjp7ImlEIjpbXX0sImN2Ijp7IktWIjpbXSwiRDAi
+OltdfSwiZkoiOnsiRDAiOltdfSwid2EiOnsiRDAiOltdfSwiQWoiOnsiZWEiOltdfSwiS1YiOnsiRDAi
+OltdfSwiZXciOnsiZWEiOltdfSwidzYiOnsiZWEiOltdfSwiSlEiOnsia0YiOltdfSwicUUiOnsiY3Yi
+OltdLCJLViI6W10sIkQwIjpbXX0sIkdoIjp7ImN2IjpbXSwiS1YiOltdLCJEMCI6W119LCJmWSI6eyJj
+diI6W10sIktWIjpbXSwiRDAiOltdfSwicloiOnsiY3YiOltdLCJLViI6W10sIkQwIjpbXX0sIlFQIjp7
+ImN2IjpbXSwiS1YiOltdLCJEMCI6W119LCJueCI6eyJLViI6W10sIkQwIjpbXX0sIlFGIjp7IktWIjpb
+XSwiRDAiOltdfSwiSUIiOnsidG4iOlsiWloiXX0sInd6Ijp7ImxEIjpbIjEiXSwiek0iOlsiMSJdLCJi
+USI6WyIxIl0sImNYIjpbIjEiXSwibEQuRSI6IjEifSwiaEgiOnsiQXoiOltdfSwiaDQiOnsiY3YiOltd
+LCJLViI6W10sIkQwIjpbXX0sIlZiIjp7IktWIjpbXSwiRDAiOltdfSwiZTciOnsibEQiOlsiS1YiXSwi
+ek0iOlsiS1YiXSwiYlEiOlsiS1YiXSwiY1giOlsiS1YiXSwibEQuRSI6IktWIn0sIkJIIjp7ImxEIjpb
+IktWIl0sIkdtIjpbIktWIl0sInpNIjpbIktWIl0sIlhqIjpbIktWIl0sImJRIjpbIktWIl0sImNYIjpb
+IktWIl0sImxELkUiOiJLViIsIkdtLkUiOiJLViJ9LCJTTiI6eyJjdiI6W10sIktWIjpbXSwiRDAiOltd
+fSwibHAiOnsiY3YiOltdLCJLViI6W10sIkQwIjpbXX0sIlRiIjp7ImN2IjpbXSwiS1YiOltdLCJEMCI6
+W119LCJJdiI6eyJjdiI6W10sIktWIjpbXSwiRDAiOltdfSwiV1AiOnsiY3YiOltdLCJLViI6W10sIkQw
+IjpbXX0sInlZIjp7ImN2IjpbXSwiS1YiOltdLCJEMCI6W119LCJLNSI6eyJ2NiI6W10sIkQwIjpbXX0s
+IkNtIjp7IkQwIjpbXX0sIkNRIjp7IktWIjpbXSwiRDAiOltdfSwidzQiOnsidG4iOlsiWloiXX0sInJo
+Ijp7ImxEIjpbIktWIl0sIkdtIjpbIktWIl0sInpNIjpbIktWIl0sIlhqIjpbIktWIl0sImJRIjpbIktW
+Il0sImNYIjpbIktWIl0sImxELkUiOiJLViIsIkdtLkUiOiJLViJ9LCJjZiI6eyJZayI6WyJxVSIsInFV
+Il0sIlowIjpbInFVIiwicVUiXX0sImk3Ijp7IllrIjpbInFVIiwicVUiXSwiWjAiOlsicVUiLCJxVSJd
+LCJZay5LIjoicVUiLCJZay5WIjoicVUifSwiU3kiOnsiWWsiOlsicVUiLCJxVSJdLCJaMCI6WyJxVSIs
+InFVIl0sIllrLksiOiJxVSIsIllrLlYiOiJxVSJ9LCJJNCI6eyJsZiI6WyJxVSJdLCJ4dSI6WyJxVSJd
+LCJiUSI6WyJxVSJdLCJjWCI6WyJxVSJdLCJsZi5FIjoicVUifSwiUk8iOnsicWgiOlsiMSJdfSwiQ3Ei
+OnsiUk8iOlsiMSJdLCJxaCI6WyIxIl19LCJ4QyI6eyJNTyI6WyIxIl19LCJ2RCI6eyJrRiI6W119LCJt
+NiI6eyJrRiI6W119LCJjdCI6eyJrRiI6W119LCJPdyI6eyJrRiI6W119LCJXOSI6eyJBbiI6WyIxIl19
+LCJkVyI6eyJ2NiI6W10sIkQwIjpbXX0sIm1rIjp7InkwIjpbXX0sIktvIjp7Im9uIjpbXX0sIkFzIjp7
+ImxmIjpbInFVIl0sInh1IjpbInFVIl0sImJRIjpbInFVIl0sImNYIjpbInFVIl19LCJyNyI6eyJFNCI6
+W119LCJUeiI6eyJsRCI6WyIxIl0sInpNIjpbIjEiXSwiYlEiOlsiMSJdLCJFNCI6W10sImNYIjpbIjEi
+XSwibEQuRSI6IjEifSwibmQiOnsiaGkiOltdLCJjdiI6W10sIktWIjpbXSwiRDAiOltdfSwiS2UiOnsi
+bGYiOlsicVUiXSwieHUiOlsicVUiXSwiYlEiOlsicVUiXSwiY1giOlsicVUiXSwibGYuRSI6InFVIn0s
+ImhpIjp7ImN2IjpbXSwiS1YiOltdLCJEMCI6W119LCJRVyI6eyJYUyI6W10sIlJ6IjpbXX0sIlhBIjp7
+ImtGIjpbXX0sInZ0Ijp7IkQ4IjpbXX0sImNEIjp7IkQ4IjpbXX0sImR2Ijp7IlJ6IjpbXX0sIk9GIjp7
+ImZ2IjpbXX0sInJ1Ijp7ImZ2IjpbXX0sIklWIjp7ImZ2IjpbXX0sIm42Ijp7InpNIjpbIklqIl0sImJR
+IjpbIklqIl0sImNYIjpbIklqIl0sIkFTIjpbXX19JykpCkguRkYodi50eXBlVW5pdmVyc2UsSlNPTi5w
+YXJzZSgneyJ3MiI6MSwiUUMiOjIsIkxaIjoxLCJrVCI6MiwibVciOjEsInV5IjoxLCJpbCI6MiwiVmoi
+OjEsIlh2IjoxLCJuWSI6MSwiV1kiOjEsInBSIjoxLCJ2ZyI6MX0nKSkKdmFyIHU9e2w6IkNhbm5vdCBl
+eHRyYWN0IGEgZmlsZSBwYXRoIGZyb20gYSBVUkkgd2l0aCBhIGZyYWdtZW50IGNvbXBvbmVudCIsaToi
+Q2Fubm90IGV4dHJhY3QgYSBmaWxlIHBhdGggZnJvbSBhIFVSSSB3aXRoIGEgcXVlcnkgY29tcG9uZW50
+IixqOiJDYW5ub3QgZXh0cmFjdCBhIG5vbi1XaW5kb3dzIGZpbGUgcGF0aCBmcm9tIGEgZmlsZSBVUkkg
+d2l0aCBhbiBhdXRob3JpdHkiLGQ6ImFyZWEtYW5hbHl6ZXIsYW5hbHl6ZXItbm5iZC1taWdyYXRpb24s
+dHlwZS1idWcifQp2YXIgdD0oZnVuY3Rpb24gcnRpaSgpe3ZhciBzPUguTjAKcmV0dXJue2JxOnMoIkdo
+IiksbjpzKCJDdyIpLGNSOnMoInJaIiksdzpzKCJBeiIpLGs6cygiUVAiKSxnRjpzKCJQRDxHRCxAPiIp
+LFc6cygiYlE8QD4iKSxoOnMoImN2IiksbTpzKCJYUyIpLEI6cygiZWEiKSxnODpzKCJSeiIpLGM4OnMo
+ImhIIiksWTpzKCJFSCIpLGk6cygiYjg8QD4iKSxKOnMoIkxMIiksZ3A6cygiSDciKSxJOnMoIlNnIiks
+bzpzKCJ2USIpLGVoOnMoImNYPEtWPiIpLE86cygiY1g8cVU+IiksUjpzKCJjWDxAPiIpLGZBOnMoImpk
+PFNlPiIpLGdpOnMoImpkPGo4PiIpLGQ6cygiamQ8WjA8cVUsTWg/Pj4iKSxmaDpzKCJqZDxEOD4iKSxy
+OnMoImpkPGtGPiIpLHM6cygiamQ8cVU+IiksaGg6cygiamQ8eUQ+IiksYUo6cygiamQ8d2I+IiksZ046
+cygiamQ8bjY+IiksYjpzKCJqZDxAPiIpLHQ6cygiamQ8SWo+IiksZDQ6cygiamQ8cVU/PiIpLFQ6cygi
+d2UiKSxlSDpzKCJ2bSIpLHU6cygiYzUiKSxhVTpzKCJYajxAPiIpLGFtOnMoIlR6PEA+IiksZW86cygi
+TjU8R0QsQD4iKSxkejpzKCJoRiIpLGY0OnMoInpNPGo4PiIpLHg6cygiek08WjA8cVUsTWg/Pj4iKSxl
+dzpzKCJ6TTxNaD4iKSxhOnMoInpNPHFVPiIpLGo6cygiek08QD4iKSxMOnMoInpNPElqPiIpLGVlOnMo
+InpNPE1oPz4iKSxGOnMoInU4IiksaDY6cygiWjA8cVUsTWg+IiksdjpzKCJaMDxxVSxxVT4iKSxmOnMo
+IlowPEAsQD4iKSxHOnMoIlowPHFVLE1oPz4iKSxEOnMoImxKPHFVLHFVPiIpLGRvOnMoImxKPHFVLEA+
+IiksVjpzKCJBaiIpLGRFOnMoIkVUIiksQTpzKCJLViIpLEU6cygia0YiKSxQOnMoImM4IiksSzpzKCJN
+aCIpLHA6cygiZXciKSxxOnMoInRuPFpaPiIpLGZ2OnMoIndMIiksY3o6cygiaWIiKSxhTzpzKCJuZCIp
+LEM6cygieHU8cVU+IiksbDpzKCJHeiIpLE46cygicVUiKSxkRzpzKCJxVShxVSkiKSxnNzpzKCJoaSIp
+LGZvOnMoIkdEIiksYVc6cygieVkiKSxhazpzKCJBUyIpLGdjOnMoIm42IiksYko6cygia2QiKSxkdzpz
+KCJHajxxVSxxVT4iKSxkRDpzKCJpRCIpLGVKOnMoInU2PHFVPiIpLGc0OnMoIks1IiksY2k6cygidjYi
+KSxnMjpzKCJDbSIpLGJqOnMoIlpmPGZKPiIpLGg5OnMoIkNRIiksYWM6cygiZTciKSxROnMoIkNxPEFq
+PiIpLFU6cygid3o8Y3Y+IiksYW86cygidnM8Zko+IiksYzpzKCJ2czxAPiIpLGZKOnMoInZzPElqPiIp
+LGNyOnMoIkpRIikseTpzKCJhMiIpLGFsOnMoImEyKE1oKSIpLGdSOnMoIkNQIiksejpzKCJAIiksZk86
+cygiQCgpIiksYkk6cygiQChNaCkiKSxhZzpzKCJAKE1oLEd6KSIpLGJVOnMoIkAoeHU8cVU+KSIpLGRP
+OnMoIkAocVUpIiksYjg6cygiQChALEApIiksUzpzKCJJaiIpLGF3OnMoIjAmKiIpLF86cygiTWgqIiks
+Y2g6cygiRDA/IiksYkc6cygiYjg8Yzg+PyIpLGVzOnMoIkU0PyIpLGJrOnMoInpNPHFVPj8iKSxiTTpz
+KCJ6TTxAPj8iKSxjWjpzKCJaMDxxVSxxVT4/IiksYzk6cygiWjA8cVUsQD4/IiksZm46cygiWjA8cVUs
+TWg/Pj8iKSxYOnMoIk1oPyIpLGRrOnMoInFVPyIpLGU6cygiRmU8QCxAPj8iKSxnOnMoImJuPyIpLGI3
+OnMoImEyKE1oKT8iKSxidzpzKCJAKGVhKT8iKSxmVjpzKCJNaD8oTWg/LE1oPyk/IiksZEE6cygiTWg/
+KEApPyIpLFo6cygifigpPyIpLGd4OnMoIn4oZXcpPyIpLGRpOnMoIlpaIiksSDpzKCJ+IiksTTpzKCJ+
+KCkiKSxlQTpzKCJ+KHFVLHFVKSIpLGNBOnMoIn4ocVUsQCkiKX19KSgpOyhmdW5jdGlvbiBjb25zdGFu
+dHMoKXt2YXIgcz1odW5rSGVscGVycy5tYWtlQ29uc3RMaXN0CkMueG49Vy5HaC5wcm90b3R5cGUKQy5S
+WT1XLlFQLnByb3RvdHlwZQpDLm1IPVcuYWUucHJvdG90eXBlCkMuQlo9Vy5WYi5wcm90b3R5cGUKQy5E
+dD1XLmZKLnByb3RvdHlwZQpDLk9rPUouR3YucHJvdG90eXBlCkMuTm09Si5qZC5wcm90b3R5cGUKQy5q
+bj1KLmJVLnByb3RvdHlwZQpDLkNEPUoucUkucHJvdG90eXBlCkMueEI9Si5Eci5wcm90b3R5cGUKQy5E
+Rz1KLmM1LnByb3RvdHlwZQpDLkV4PVcudTgucHJvdG90eXBlCkMuTkE9SC5WNi5wcm90b3R5cGUKQy50
+NT1XLkJILnByb3RvdHlwZQpDLkx0PVcuU04ucHJvdG90eXBlCkMuWlE9Si5pQy5wcm90b3R5cGUKQy5J
+ZT1XLlRiLnByb3RvdHlwZQpDLnZCPUoua2QucHJvdG90eXBlCkMub2w9Vy5LNS5wcm90b3R5cGUKQy55
+OD1uZXcgUC5VOCgpCkMuaDk9bmV3IFAuQ1YoKQpDLkd3PW5ldyBILkZ1KEguTjAoIkZ1PDAmPiIpKQpD
+Lk80PWZ1bmN0aW9uIGdldFRhZ0ZhbGxiYWNrKG8pIHsKICB2YXIgcyA9IE9iamVjdC5wcm90b3R5cGUu
+dG9TdHJpbmcuY2FsbChvKTsKICByZXR1cm4gcy5zdWJzdHJpbmcoOCwgcy5sZW5ndGggLSAxKTsKfQpD
+LllxPWZ1bmN0aW9uKCkgewogIHZhciB0b1N0cmluZ0Z1bmN0aW9uID0gT2JqZWN0LnByb3RvdHlwZS50
+b1N0cmluZzsKICBmdW5jdGlvbiBnZXRUYWcobykgewogICAgdmFyIHMgPSB0b1N0cmluZ0Z1bmN0aW9u
+LmNhbGwobyk7CiAgICByZXR1cm4gcy5zdWJzdHJpbmcoOCwgcy5sZW5ndGggLSAxKTsKICB9CiAgZnVu
+Y3Rpb24gZ2V0VW5rbm93blRhZyhvYmplY3QsIHRhZykgewogICAgaWYgKC9eSFRNTFtBLVpdLipFbGVt
+ZW50JC8udGVzdCh0YWcpKSB7CiAgICAgIHZhciBuYW1lID0gdG9TdHJpbmdGdW5jdGlvbi5jYWxsKG9i
+amVjdCk7CiAgICAgIGlmIChuYW1lID09ICJbb2JqZWN0IE9iamVjdF0iKSByZXR1cm4gbnVsbDsKICAg
+ICAgcmV0dXJuICJIVE1MRWxlbWVudCI7CiAgICB9CiAgfQogIGZ1bmN0aW9uIGdldFVua25vd25UYWdH
+ZW5lcmljQnJvd3NlcihvYmplY3QsIHRhZykgewogICAgaWYgKHNlbGYuSFRNTEVsZW1lbnQgJiYgb2Jq
+ZWN0IGluc3RhbmNlb2YgSFRNTEVsZW1lbnQpIHJldHVybiAiSFRNTEVsZW1lbnQiOwogICAgcmV0dXJu
+IGdldFVua25vd25UYWcob2JqZWN0LCB0YWcpOwogIH0KICBmdW5jdGlvbiBwcm90b3R5cGVGb3JUYWco
+dGFnKSB7CiAgICBpZiAodHlwZW9mIHdpbmRvdyA9PSAidW5kZWZpbmVkIikgcmV0dXJuIG51bGw7CiAg
+ICBpZiAodHlwZW9mIHdpbmRvd1t0YWddID09ICJ1bmRlZmluZWQiKSByZXR1cm4gbnVsbDsKICAgIHZh
+ciBjb25zdHJ1Y3RvciA9IHdpbmRvd1t0YWddOwogICAgaWYgKHR5cGVvZiBjb25zdHJ1Y3RvciAhPSAi
+ZnVuY3Rpb24iKSByZXR1cm4gbnVsbDsKICAgIHJldHVybiBjb25zdHJ1Y3Rvci5wcm90b3R5cGU7CiAg
+fQogIGZ1bmN0aW9uIGRpc2NyaW1pbmF0b3IodGFnKSB7IHJldHVybiBudWxsOyB9CiAgdmFyIGlzQnJv
+d3NlciA9IHR5cGVvZiBuYXZpZ2F0b3IgPT0gIm9iamVjdCI7CiAgcmV0dXJuIHsKICAgIGdldFRhZzog
+Z2V0VGFnLAogICAgZ2V0VW5rbm93blRhZzogaXNCcm93c2VyID8gZ2V0VW5rbm93blRhZ0dlbmVyaWNC
+cm93c2VyIDogZ2V0VW5rbm93blRhZywKICAgIHByb3RvdHlwZUZvclRhZzogcHJvdG90eXBlRm9yVGFn
+LAogICAgZGlzY3JpbWluYXRvcjogZGlzY3JpbWluYXRvciB9Owp9CkMud2I9ZnVuY3Rpb24oZ2V0VGFn
+RmFsbGJhY2spIHsKICByZXR1cm4gZnVuY3Rpb24oaG9va3MpIHsKICAgIGlmICh0eXBlb2YgbmF2aWdh
+dG9yICE9ICJvYmplY3QiKSByZXR1cm4gaG9va3M7CiAgICB2YXIgdWEgPSBuYXZpZ2F0b3IudXNlckFn
+ZW50OwogICAgaWYgKHVhLmluZGV4T2YoIkR1bXBSZW5kZXJUcmVlIikgPj0gMCkgcmV0dXJuIGhvb2tz
+OwogICAgaWYgKHVhLmluZGV4T2YoIkNocm9tZSIpID49IDApIHsKICAgICAgZnVuY3Rpb24gY29uZmly
+bShwKSB7CiAgICAgICAgcmV0dXJuIHR5cGVvZiB3aW5kb3cgPT0gIm9iamVjdCIgJiYgd2luZG93W3Bd
+ICYmIHdpbmRvd1twXS5uYW1lID09IHA7CiAgICAgIH0KICAgICAgaWYgKGNvbmZpcm0oIldpbmRvdyIp
+ICYmIGNvbmZpcm0oIkhUTUxFbGVtZW50IikpIHJldHVybiBob29rczsKICAgIH0KICAgIGhvb2tzLmdl
+dFRhZyA9IGdldFRhZ0ZhbGxiYWNrOwogIH07Cn0KQy5LVT1mdW5jdGlvbihob29rcykgewogIGlmICh0
+eXBlb2YgZGFydEV4cGVyaW1lbnRhbEZpeHVwR2V0VGFnICE9ICJmdW5jdGlvbiIpIHJldHVybiBob29r
+czsKICBob29rcy5nZXRUYWcgPSBkYXJ0RXhwZXJpbWVudGFsRml4dXBHZXRUYWcoaG9va3MuZ2V0VGFn
+KTsKfQpDLmZRPWZ1bmN0aW9uKGhvb2tzKSB7CiAgdmFyIGdldFRhZyA9IGhvb2tzLmdldFRhZzsKICB2
+YXIgcHJvdG90eXBlRm9yVGFnID0gaG9va3MucHJvdG90eXBlRm9yVGFnOwogIGZ1bmN0aW9uIGdldFRh
+Z0ZpeGVkKG8pIHsKICAgIHZhciB0YWcgPSBnZXRUYWcobyk7CiAgICBpZiAodGFnID09ICJEb2N1bWVu
+dCIpIHsKICAgICAgaWYgKCEhby54bWxWZXJzaW9uKSByZXR1cm4gIiFEb2N1bWVudCI7CiAgICAgIHJl
+dHVybiAiIUhUTUxEb2N1bWVudCI7CiAgICB9CiAgICByZXR1cm4gdGFnOwogIH0KICBmdW5jdGlvbiBw
+cm90b3R5cGVGb3JUYWdGaXhlZCh0YWcpIHsKICAgIGlmICh0YWcgPT0gIkRvY3VtZW50IikgcmV0dXJu
+IG51bGw7CiAgICByZXR1cm4gcHJvdG90eXBlRm9yVGFnKHRhZyk7CiAgfQogIGhvb2tzLmdldFRhZyA9
+IGdldFRhZ0ZpeGVkOwogIGhvb2tzLnByb3RvdHlwZUZvclRhZyA9IHByb3RvdHlwZUZvclRhZ0ZpeGVk
+Owp9CkMuZGs9ZnVuY3Rpb24oaG9va3MpIHsKICB2YXIgdXNlckFnZW50ID0gdHlwZW9mIG5hdmlnYXRv
+ciA9PSAib2JqZWN0IiA/IG5hdmlnYXRvci51c2VyQWdlbnQgOiAiIjsKICBpZiAodXNlckFnZW50Lmlu
+ZGV4T2YoIkZpcmVmb3giKSA9PSAtMSkgcmV0dXJuIGhvb2tzOwogIHZhciBnZXRUYWcgPSBob29rcy5n
+ZXRUYWc7CiAgdmFyIHF1aWNrTWFwID0gewogICAgIkJlZm9yZVVubG9hZEV2ZW50IjogIkV2ZW50IiwK
+ICAgICJEYXRhVHJhbnNmZXIiOiAiQ2xpcGJvYXJkIiwKICAgICJHZW9HZW9sb2NhdGlvbiI6ICJHZW9s
+b2NhdGlvbiIsCiAgICAiTG9jYXRpb24iOiAiIUxvY2F0aW9uIiwKICAgICJXb3JrZXJNZXNzYWdlRXZl
+bnQiOiAiTWVzc2FnZUV2ZW50IiwKICAgICJYTUxEb2N1bWVudCI6ICIhRG9jdW1lbnQifTsKICBmdW5j
+dGlvbiBnZXRUYWdGaXJlZm94KG8pIHsKICAgIHZhciB0YWcgPSBnZXRUYWcobyk7CiAgICByZXR1cm4g
+cXVpY2tNYXBbdGFnXSB8fCB0YWc7CiAgfQogIGhvb2tzLmdldFRhZyA9IGdldFRhZ0ZpcmVmb3g7Cn0K
+Qy54aT1mdW5jdGlvbihob29rcykgewogIHZhciB1c2VyQWdlbnQgPSB0eXBlb2YgbmF2aWdhdG9yID09
+ICJvYmplY3QiID8gbmF2aWdhdG9yLnVzZXJBZ2VudCA6ICIiOwogIGlmICh1c2VyQWdlbnQuaW5kZXhP
+ZigiVHJpZGVudC8iKSA9PSAtMSkgcmV0dXJuIGhvb2tzOwogIHZhciBnZXRUYWcgPSBob29rcy5nZXRU
+YWc7CiAgdmFyIHF1aWNrTWFwID0gewogICAgIkJlZm9yZVVubG9hZEV2ZW50IjogIkV2ZW50IiwKICAg
+ICJEYXRhVHJhbnNmZXIiOiAiQ2xpcGJvYXJkIiwKICAgICJIVE1MRERFbGVtZW50IjogIkhUTUxFbGVt
+ZW50IiwKICAgICJIVE1MRFRFbGVtZW50IjogIkhUTUxFbGVtZW50IiwKICAgICJIVE1MUGhyYXNlRWxl
+bWVudCI6ICJIVE1MRWxlbWVudCIsCiAgICAiUG9zaXRpb24iOiAiR2VvcG9zaXRpb24iCiAgfTsKICBm
+dW5jdGlvbiBnZXRUYWdJRShvKSB7CiAgICB2YXIgdGFnID0gZ2V0VGFnKG8pOwogICAgdmFyIG5ld1Rh
+ZyA9IHF1aWNrTWFwW3RhZ107CiAgICBpZiAobmV3VGFnKSByZXR1cm4gbmV3VGFnOwogICAgaWYgKHRh
+ZyA9PSAiT2JqZWN0IikgewogICAgICBpZiAod2luZG93LkRhdGFWaWV3ICYmIChvIGluc3RhbmNlb2Yg
+d2luZG93LkRhdGFWaWV3KSkgcmV0dXJuICJEYXRhVmlldyI7CiAgICB9CiAgICByZXR1cm4gdGFnOwog
+IH0KICBmdW5jdGlvbiBwcm90b3R5cGVGb3JUYWdJRSh0YWcpIHsKICAgIHZhciBjb25zdHJ1Y3RvciA9
+IHdpbmRvd1t0YWddOwogICAgaWYgKGNvbnN0cnVjdG9yID09IG51bGwpIHJldHVybiBudWxsOwogICAg
+cmV0dXJuIGNvbnN0cnVjdG9yLnByb3RvdHlwZTsKICB9CiAgaG9va3MuZ2V0VGFnID0gZ2V0VGFnSUU7
+CiAgaG9va3MucHJvdG90eXBlRm9yVGFnID0gcHJvdG90eXBlRm9yVGFnSUU7Cn0KQy5pNz1mdW5jdGlv
+bihob29rcykgeyByZXR1cm4gaG9va3M7IH0KCkMuQ3Q9bmV3IFAuYnkoKQpDLkVxPW5ldyBQLms1KCkK
+Qy54TT1uZXcgUC51NSgpCkMuUWs9bmV3IFAuRTMoKQpDLk52PW5ldyBILmtyKCkKQy5OVT1uZXcgUC5K
+aSgpCkMucGQ9bmV3IFAuWmQoKQpDLkFkPW5ldyBSLkg3KDAsIkhpbnRBY3Rpb25LaW5kLmFkZE51bGxh
+YmxlSGludCIpCkMubmU9bmV3IFIuSDcoMSwiSGludEFjdGlvbktpbmQuYWRkTm9uTnVsbGFibGVIaW50
+IikKQy5teT1uZXcgUi5INygyLCJIaW50QWN0aW9uS2luZC5jaGFuZ2VUb051bGxhYmxlSGludCIpCkMu
+cng9bmV3IFIuSDcoMywiSGludEFjdGlvbktpbmQuY2hhbmdlVG9Ob25OdWxsYWJsZUhpbnQiKQpDLndW
+PW5ldyBSLkg3KDQsIkhpbnRBY3Rpb25LaW5kLnJlbW92ZU51bGxhYmxlSGludCIpCkMuZlI9bmV3IFIu
+SDcoNSwiSGludEFjdGlvbktpbmQucmVtb3ZlTm9uTnVsbGFibGVIaW50IikKQy5BMz1uZXcgUC5NeChu
+dWxsKQpDLm5YPW5ldyBQLm9qKG51bGwpCkMuY3c9bmV3IEwuR2IoMCwiVW5pdE1pZ3JhdGlvblN0YXR1
+cy5hbHJlYWR5TWlncmF0ZWQiKQpDLmRjPW5ldyBMLkdiKDEsIlVuaXRNaWdyYXRpb25TdGF0dXMuaW5k
+ZXRlcm1pbmF0ZSIpCkMuV0Q9bmV3IEwuR2IoMiwiVW5pdE1pZ3JhdGlvblN0YXR1cy5taWdyYXRpbmci
+KQpDLlhqPW5ldyBMLkdiKDMsIlVuaXRNaWdyYXRpb25TdGF0dXMub3B0aW5nT3V0IikKQy5sMD1ILlFJ
+KHMoW0MuY3csQy5kYyxDLldELEMuWGpdKSxILk4wKCJqZDxHYj4iKSkKQy5haz1ILlFJKHMoWzAsMCwz
+Mjc3NiwzMzc5MiwxLDEwMjQwLDAsMF0pLHQudCkKQy5jbT1ILlFJKHMoWyIqOjpjbGFzcyIsIio6OmRp
+ciIsIio6OmRyYWdnYWJsZSIsIio6OmhpZGRlbiIsIio6OmlkIiwiKjo6aW5lcnQiLCIqOjppdGVtcHJv
+cCIsIio6Oml0ZW1yZWYiLCIqOjppdGVtc2NvcGUiLCIqOjpsYW5nIiwiKjo6c3BlbGxjaGVjayIsIio6
+OnRpdGxlIiwiKjo6dHJhbnNsYXRlIiwiQTo6YWNjZXNza2V5IiwiQTo6Y29vcmRzIiwiQTo6aHJlZmxh
+bmciLCJBOjpuYW1lIiwiQTo6c2hhcGUiLCJBOjp0YWJpbmRleCIsIkE6OnRhcmdldCIsIkE6OnR5cGUi
+LCJBUkVBOjphY2Nlc3NrZXkiLCJBUkVBOjphbHQiLCJBUkVBOjpjb29yZHMiLCJBUkVBOjpub2hyZWYi
+LCJBUkVBOjpzaGFwZSIsIkFSRUE6OnRhYmluZGV4IiwiQVJFQTo6dGFyZ2V0IiwiQVVESU86OmNvbnRy
+b2xzIiwiQVVESU86Omxvb3AiLCJBVURJTzo6bWVkaWFncm91cCIsIkFVRElPOjptdXRlZCIsIkFVRElP
+OjpwcmVsb2FkIiwiQkRPOjpkaXIiLCJCT0RZOjphbGluayIsIkJPRFk6OmJnY29sb3IiLCJCT0RZOjps
+aW5rIiwiQk9EWTo6dGV4dCIsIkJPRFk6OnZsaW5rIiwiQlI6OmNsZWFyIiwiQlVUVE9OOjphY2Nlc3Nr
+ZXkiLCJCVVRUT046OmRpc2FibGVkIiwiQlVUVE9OOjpuYW1lIiwiQlVUVE9OOjp0YWJpbmRleCIsIkJV
+VFRPTjo6dHlwZSIsIkJVVFRPTjo6dmFsdWUiLCJDQU5WQVM6OmhlaWdodCIsIkNBTlZBUzo6d2lkdGgi
+LCJDQVBUSU9OOjphbGlnbiIsIkNPTDo6YWxpZ24iLCJDT0w6OmNoYXIiLCJDT0w6OmNoYXJvZmYiLCJD
+T0w6OnNwYW4iLCJDT0w6OnZhbGlnbiIsIkNPTDo6d2lkdGgiLCJDT0xHUk9VUDo6YWxpZ24iLCJDT0xH
+Uk9VUDo6Y2hhciIsIkNPTEdST1VQOjpjaGFyb2ZmIiwiQ09MR1JPVVA6OnNwYW4iLCJDT0xHUk9VUDo6
+dmFsaWduIiwiQ09MR1JPVVA6OndpZHRoIiwiQ09NTUFORDo6Y2hlY2tlZCIsIkNPTU1BTkQ6OmNvbW1h
+bmQiLCJDT01NQU5EOjpkaXNhYmxlZCIsIkNPTU1BTkQ6OmxhYmVsIiwiQ09NTUFORDo6cmFkaW9ncm91
+cCIsIkNPTU1BTkQ6OnR5cGUiLCJEQVRBOjp2YWx1ZSIsIkRFTDo6ZGF0ZXRpbWUiLCJERVRBSUxTOjpv
+cGVuIiwiRElSOjpjb21wYWN0IiwiRElWOjphbGlnbiIsIkRMOjpjb21wYWN0IiwiRklFTERTRVQ6OmRp
+c2FibGVkIiwiRk9OVDo6Y29sb3IiLCJGT05UOjpmYWNlIiwiRk9OVDo6c2l6ZSIsIkZPUk06OmFjY2Vw
+dCIsIkZPUk06OmF1dG9jb21wbGV0ZSIsIkZPUk06OmVuY3R5cGUiLCJGT1JNOjptZXRob2QiLCJGT1JN
+OjpuYW1lIiwiRk9STTo6bm92YWxpZGF0ZSIsIkZPUk06OnRhcmdldCIsIkZSQU1FOjpuYW1lIiwiSDE6
+OmFsaWduIiwiSDI6OmFsaWduIiwiSDM6OmFsaWduIiwiSDQ6OmFsaWduIiwiSDU6OmFsaWduIiwiSDY6
+OmFsaWduIiwiSFI6OmFsaWduIiwiSFI6Om5vc2hhZGUiLCJIUjo6c2l6ZSIsIkhSOjp3aWR0aCIsIkhU
+TUw6OnZlcnNpb24iLCJJRlJBTUU6OmFsaWduIiwiSUZSQU1FOjpmcmFtZWJvcmRlciIsIklGUkFNRTo6
+aGVpZ2h0IiwiSUZSQU1FOjptYXJnaW5oZWlnaHQiLCJJRlJBTUU6Om1hcmdpbndpZHRoIiwiSUZSQU1F
+Ojp3aWR0aCIsIklNRzo6YWxpZ24iLCJJTUc6OmFsdCIsIklNRzo6Ym9yZGVyIiwiSU1HOjpoZWlnaHQi
+LCJJTUc6OmhzcGFjZSIsIklNRzo6aXNtYXAiLCJJTUc6Om5hbWUiLCJJTUc6OnVzZW1hcCIsIklNRzo6
+dnNwYWNlIiwiSU1HOjp3aWR0aCIsIklOUFVUOjphY2NlcHQiLCJJTlBVVDo6YWNjZXNza2V5IiwiSU5Q
+VVQ6OmFsaWduIiwiSU5QVVQ6OmFsdCIsIklOUFVUOjphdXRvY29tcGxldGUiLCJJTlBVVDo6YXV0b2Zv
+Y3VzIiwiSU5QVVQ6OmNoZWNrZWQiLCJJTlBVVDo6ZGlzYWJsZWQiLCJJTlBVVDo6aW5wdXRtb2RlIiwi
+SU5QVVQ6OmlzbWFwIiwiSU5QVVQ6Omxpc3QiLCJJTlBVVDo6bWF4IiwiSU5QVVQ6Om1heGxlbmd0aCIs
+IklOUFVUOjptaW4iLCJJTlBVVDo6bXVsdGlwbGUiLCJJTlBVVDo6bmFtZSIsIklOUFVUOjpwbGFjZWhv
+bGRlciIsIklOUFVUOjpyZWFkb25seSIsIklOUFVUOjpyZXF1aXJlZCIsIklOUFVUOjpzaXplIiwiSU5Q
+VVQ6OnN0ZXAiLCJJTlBVVDo6dGFiaW5kZXgiLCJJTlBVVDo6dHlwZSIsIklOUFVUOjp1c2VtYXAiLCJJ
+TlBVVDo6dmFsdWUiLCJJTlM6OmRhdGV0aW1lIiwiS0VZR0VOOjpkaXNhYmxlZCIsIktFWUdFTjo6a2V5
+dHlwZSIsIktFWUdFTjo6bmFtZSIsIkxBQkVMOjphY2Nlc3NrZXkiLCJMQUJFTDo6Zm9yIiwiTEVHRU5E
+OjphY2Nlc3NrZXkiLCJMRUdFTkQ6OmFsaWduIiwiTEk6OnR5cGUiLCJMSTo6dmFsdWUiLCJMSU5LOjpz
+aXplcyIsIk1BUDo6bmFtZSIsIk1FTlU6OmNvbXBhY3QiLCJNRU5VOjpsYWJlbCIsIk1FTlU6OnR5cGUi
+LCJNRVRFUjo6aGlnaCIsIk1FVEVSOjpsb3ciLCJNRVRFUjo6bWF4IiwiTUVURVI6Om1pbiIsIk1FVEVS
+Ojp2YWx1ZSIsIk9CSkVDVDo6dHlwZW11c3RtYXRjaCIsIk9MOjpjb21wYWN0IiwiT0w6OnJldmVyc2Vk
+IiwiT0w6OnN0YXJ0IiwiT0w6OnR5cGUiLCJPUFRHUk9VUDo6ZGlzYWJsZWQiLCJPUFRHUk9VUDo6bGFi
+ZWwiLCJPUFRJT046OmRpc2FibGVkIiwiT1BUSU9OOjpsYWJlbCIsIk9QVElPTjo6c2VsZWN0ZWQiLCJP
+UFRJT046OnZhbHVlIiwiT1VUUFVUOjpmb3IiLCJPVVRQVVQ6Om5hbWUiLCJQOjphbGlnbiIsIlBSRTo6
+d2lkdGgiLCJQUk9HUkVTUzo6bWF4IiwiUFJPR1JFU1M6Om1pbiIsIlBST0dSRVNTOjp2YWx1ZSIsIlNF
+TEVDVDo6YXV0b2NvbXBsZXRlIiwiU0VMRUNUOjpkaXNhYmxlZCIsIlNFTEVDVDo6bXVsdGlwbGUiLCJT
+RUxFQ1Q6Om5hbWUiLCJTRUxFQ1Q6OnJlcXVpcmVkIiwiU0VMRUNUOjpzaXplIiwiU0VMRUNUOjp0YWJp
+bmRleCIsIlNPVVJDRTo6dHlwZSIsIlRBQkxFOjphbGlnbiIsIlRBQkxFOjpiZ2NvbG9yIiwiVEFCTEU6
+OmJvcmRlciIsIlRBQkxFOjpjZWxscGFkZGluZyIsIlRBQkxFOjpjZWxsc3BhY2luZyIsIlRBQkxFOjpm
+cmFtZSIsIlRBQkxFOjpydWxlcyIsIlRBQkxFOjpzdW1tYXJ5IiwiVEFCTEU6OndpZHRoIiwiVEJPRFk6
+OmFsaWduIiwiVEJPRFk6OmNoYXIiLCJUQk9EWTo6Y2hhcm9mZiIsIlRCT0RZOjp2YWxpZ24iLCJURDo6
+YWJiciIsIlREOjphbGlnbiIsIlREOjpheGlzIiwiVEQ6OmJnY29sb3IiLCJURDo6Y2hhciIsIlREOjpj
+aGFyb2ZmIiwiVEQ6OmNvbHNwYW4iLCJURDo6aGVhZGVycyIsIlREOjpoZWlnaHQiLCJURDo6bm93cmFw
+IiwiVEQ6OnJvd3NwYW4iLCJURDo6c2NvcGUiLCJURDo6dmFsaWduIiwiVEQ6OndpZHRoIiwiVEVYVEFS
+RUE6OmFjY2Vzc2tleSIsIlRFWFRBUkVBOjphdXRvY29tcGxldGUiLCJURVhUQVJFQTo6Y29scyIsIlRF
+WFRBUkVBOjpkaXNhYmxlZCIsIlRFWFRBUkVBOjppbnB1dG1vZGUiLCJURVhUQVJFQTo6bmFtZSIsIlRF
+WFRBUkVBOjpwbGFjZWhvbGRlciIsIlRFWFRBUkVBOjpyZWFkb25seSIsIlRFWFRBUkVBOjpyZXF1aXJl
+ZCIsIlRFWFRBUkVBOjpyb3dzIiwiVEVYVEFSRUE6OnRhYmluZGV4IiwiVEVYVEFSRUE6OndyYXAiLCJU
+Rk9PVDo6YWxpZ24iLCJURk9PVDo6Y2hhciIsIlRGT09UOjpjaGFyb2ZmIiwiVEZPT1Q6OnZhbGlnbiIs
+IlRIOjphYmJyIiwiVEg6OmFsaWduIiwiVEg6OmF4aXMiLCJUSDo6Ymdjb2xvciIsIlRIOjpjaGFyIiwi
+VEg6OmNoYXJvZmYiLCJUSDo6Y29sc3BhbiIsIlRIOjpoZWFkZXJzIiwiVEg6OmhlaWdodCIsIlRIOjpu
+b3dyYXAiLCJUSDo6cm93c3BhbiIsIlRIOjpzY29wZSIsIlRIOjp2YWxpZ24iLCJUSDo6d2lkdGgiLCJU
+SEVBRDo6YWxpZ24iLCJUSEVBRDo6Y2hhciIsIlRIRUFEOjpjaGFyb2ZmIiwiVEhFQUQ6OnZhbGlnbiIs
+IlRSOjphbGlnbiIsIlRSOjpiZ2NvbG9yIiwiVFI6OmNoYXIiLCJUUjo6Y2hhcm9mZiIsIlRSOjp2YWxp
+Z24iLCJUUkFDSzo6ZGVmYXVsdCIsIlRSQUNLOjpraW5kIiwiVFJBQ0s6OmxhYmVsIiwiVFJBQ0s6OnNy
+Y2xhbmciLCJVTDo6Y29tcGFjdCIsIlVMOjp0eXBlIiwiVklERU86OmNvbnRyb2xzIiwiVklERU86Omhl
+aWdodCIsIlZJREVPOjpsb29wIiwiVklERU86Om1lZGlhZ3JvdXAiLCJWSURFTzo6bXV0ZWQiLCJWSURF
+Tzo6cHJlbG9hZCIsIlZJREVPOjp3aWR0aCJdKSx0LnMpCkMuVkM9SC5RSShzKFswLDAsNjU0OTAsNDUw
+NTUsNjU1MzUsMzQ4MTUsNjU1MzQsMTg0MzFdKSx0LnQpCkMubUs9SC5RSShzKFswLDAsMjY2MjQsMTAy
+Myw2NTUzNCwyMDQ3LDY1NTM0LDIwNDddKSx0LnQpCkMuU3E9SC5RSShzKFsiSEVBRCIsIkFSRUEiLCJC
+QVNFIiwiQkFTRUZPTlQiLCJCUiIsIkNPTCIsIkNPTEdST1VQIiwiRU1CRUQiLCJGUkFNRSIsIkZSQU1F
+U0VUIiwiSFIiLCJJTUFHRSIsIklNRyIsIklOUFVUIiwiSVNJTkRFWCIsIkxJTksiLCJNRVRBIiwiUEFS
+QU0iLCJTT1VSQ0UiLCJTVFlMRSIsIlRJVExFIiwiV0JSIl0pLHQucykKQy5kbj1ILlFJKHMoW10pLEgu
+TjAoImpkPExMPiIpKQpDLnhEPUguUUkocyhbXSksdC5zKQpDLmhVPUguUUkocyhbXSksdC5iKQpDLnRv
+PUguUUkocyhbMCwwLDMyNzIyLDEyMjg3LDY1NTM0LDM0ODE1LDY1NTM0LDE4NDMxXSksdC50KQpDLnJr
+PUguUUkocyhbQy5BZCxDLm5lLEMubXksQy5yeCxDLndWLEMuZlJdKSxILk4wKCJqZDxINz4iKSkKQy5G
+Mz1ILlFJKHMoWzAsMCwyNDU3NiwxMDIzLDY1NTM0LDM0ODE1LDY1NTM0LDE4NDMxXSksdC50KQpDLmVh
+PUguUUkocyhbMCwwLDMyNzU0LDExMjYzLDY1NTM0LDM0ODE1LDY1NTM0LDE4NDMxXSksdC50KQpDLlpK
+PUguUUkocyhbMCwwLDMyNzIyLDEyMjg3LDY1NTM1LDM0ODE1LDY1NTM0LDE4NDMxXSksdC50KQpDLldk
+PUguUUkocyhbMCwwLDY1NDkwLDEyMjg3LDY1NTM1LDM0ODE1LDY1NTM0LDE4NDMxXSksdC50KQpDLlF4
+PUguUUkocyhbImJpbmQiLCJpZiIsInJlZiIsInJlcGVhdCIsInN5bnRheCJdKSx0LnMpCkMuQkk9SC5R
+SShzKFsiQTo6aHJlZiIsIkFSRUE6OmhyZWYiLCJCTE9DS1FVT1RFOjpjaXRlIiwiQk9EWTo6YmFja2dy
+b3VuZCIsIkNPTU1BTkQ6Omljb24iLCJERUw6OmNpdGUiLCJGT1JNOjphY3Rpb24iLCJJTUc6OnNyYyIs
+IklOUFVUOjpzcmMiLCJJTlM6OmNpdGUiLCJROjpjaXRlIiwiVklERU86OnBvc3RlciJdKSx0LnMpCkMu
+RHg9bmV3IEguTFAoMCx7fSxDLnhELEguTjAoIkxQPHFVLHpNPGo4Pj4iKSkKQy5DTT1uZXcgSC5MUCgw
+LHt9LEMueEQsSC5OMCgiTFA8cVUscVU+IikpCkMuaUg9SC5RSShzKFtdKSxILk4wKCJqZDxHRD4iKSkK
+Qy5XTz1uZXcgSC5MUCgwLHt9LEMuaUgsSC5OMCgiTFA8R0QsQD4iKSkKQy5ZMj1uZXcgTC5POSgiTmF2
+aWdhdGlvblRyZWVOb2RlVHlwZS5kaXJlY3RvcnkiKQpDLnJmPW5ldyBMLk85KCJOYXZpZ2F0aW9uVHJl
+ZU5vZGVUeXBlLmZpbGUiKQpDLlRlPW5ldyBILnd2KCJjYWxsIikKQy5vRT1uZXcgUC5HWSghMSkKQy53
+UT1uZXcgUC5GeShudWxsLDIpfSkoKTsoZnVuY3Rpb24gc3RhdGljRmllbGRzKCl7JC56bT1udWxsCiQu
+eWo9MAokLldXPW51bGwKJC5pMD1udWxsCiQuTkY9bnVsbAokLlRYPW51bGwKJC54Nz1udWxsCiQubnc9
+bnVsbAokLnZ2PW51bGwKJC5Cdj1udWxsCiQuUzY9bnVsbAokLms4PW51bGwKJC5tZz1udWxsCiQuVUQ9
+ITEKJC5YMz1DLk5VCiQueGc9SC5RSShbXSxILk4wKCJqZDxNaD4iKSkKJC54bz1udWxsCiQuQk89bnVs
+bAokLmx0PW51bGwKJC5FVT1udWxsCiQub3I9UC5GbCh0Lk4sdC5ZKQokLklSPW51bGwKJC5JNj1udWxs
+CiQuRmY9bnVsbH0pKCk7KGZ1bmN0aW9uIGxhenlJbml0aWFsaXplcnMoKXt2YXIgcz1odW5rSGVscGVy
+cy5sYXp5RmluYWwscj1odW5rSGVscGVycy5sYXp5CnMoJCwiZmEiLCJ6IixmdW5jdGlvbigpe3JldHVy
+biBILllnKCJfJGRhcnRfZGFydENsb3N1cmUiKX0pCnMoJCwiS3EiLCJTbiIsZnVuY3Rpb24oKXtyZXR1
+cm4gSC5jTShILlM3KHsKdG9TdHJpbmc6ZnVuY3Rpb24oKXtyZXR1cm4iJHJlY2VpdmVyJCJ9fSkpfSkK
+cygkLCJ4cSIsImxxIixmdW5jdGlvbigpe3JldHVybiBILmNNKEguUzcoeyRtZXRob2QkOm51bGwsCnRv
+U3RyaW5nOmZ1bmN0aW9uKCl7cmV0dXJuIiRyZWNlaXZlciQifX0pKX0pCnMoJCwiUjEiLCJOOSIsZnVu
+Y3Rpb24oKXtyZXR1cm4gSC5jTShILlM3KG51bGwpKX0pCnMoJCwiZk4iLCJpSSIsZnVuY3Rpb24oKXty
+ZXR1cm4gSC5jTShmdW5jdGlvbigpe3ZhciAkYXJndW1lbnRzRXhwciQ9IiRhcmd1bWVudHMkIgp0cnl7
+bnVsbC4kbWV0aG9kJCgkYXJndW1lbnRzRXhwciQpfWNhdGNoKHEpe3JldHVybiBxLm1lc3NhZ2V9fSgp
+KX0pCnMoJCwicWkiLCJVTiIsZnVuY3Rpb24oKXtyZXR1cm4gSC5jTShILlM3KHZvaWQgMCkpfSkKcygk
+LCJwdiIsIlpoIixmdW5jdGlvbigpe3JldHVybiBILmNNKGZ1bmN0aW9uKCl7dmFyICRhcmd1bWVudHNF
+eHByJD0iJGFyZ3VtZW50cyQiCnRyeXsodm9pZCAwKS4kbWV0aG9kJCgkYXJndW1lbnRzRXhwciQpfWNh
+dGNoKHEpe3JldHVybiBxLm1lc3NhZ2V9fSgpKX0pCnMoJCwia3EiLCJyTiIsZnVuY3Rpb24oKXtyZXR1
+cm4gSC5jTShILk1qKG51bGwpKX0pCnMoJCwidHQiLCJjMyIsZnVuY3Rpb24oKXtyZXR1cm4gSC5jTShm
+dW5jdGlvbigpe3RyeXtudWxsLiRtZXRob2QkfWNhdGNoKHEpe3JldHVybiBxLm1lc3NhZ2V9fSgpKX0p
+CnMoJCwiZHQiLCJISyIsZnVuY3Rpb24oKXtyZXR1cm4gSC5jTShILk1qKHZvaWQgMCkpfSkKcygkLCJB
+NyIsInIxIixmdW5jdGlvbigpe3JldHVybiBILmNNKGZ1bmN0aW9uKCl7dHJ5eyh2b2lkIDApLiRtZXRo
+b2QkfWNhdGNoKHEpe3JldHVybiBxLm1lc3NhZ2V9fSgpKX0pCnMoJCwiV2MiLCJ1dCIsZnVuY3Rpb24o
+KXtyZXR1cm4gUC5PaigpfSkKcygkLCJraCIsInJmIixmdW5jdGlvbigpe3JldHVybiBuZXcgUC54cigp
+LiQwKCl9KQpzKCQsImRIIiwiSEciLGZ1bmN0aW9uKCl7cmV0dXJuIG5ldyBQLk56KCkuJDAoKX0pCnMo
+JCwiaGoiLCJWNyIsZnVuY3Rpb24oKXtyZXR1cm4gbmV3IEludDhBcnJheShILlhGKEguUUkoWy0yLC0y
+LC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0y
+LC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0xLC0yLC0yLC0yLC0y
+LC0yLDYyLC0yLDYyLC0yLDYzLDUyLDUzLDU0LDU1LDU2LDU3LDU4LDU5LDYwLDYxLC0yLC0yLC0yLC0x
+LC0yLC0yLC0yLDAsMSwyLDMsNCw1LDYsNyw4LDksMTAsMTEsMTIsMTMsMTQsMTUsMTYsMTcsMTgsMTks
+MjAsMjEsMjIsMjMsMjQsMjUsLTIsLTIsLTIsLTIsNjMsLTIsMjYsMjcsMjgsMjksMzAsMzEsMzIsMzMs
+MzQsMzUsMzYsMzcsMzgsMzksNDAsNDEsNDIsNDMsNDQsNDUsNDYsNDcsNDgsNDksNTAsNTEsLTIsLTIs
+LTIsLTIsLTJdLHQudCkpKX0pCnMoJCwiWWUiLCJ3USIsZnVuY3Rpb24oKXtyZXR1cm4gdHlwZW9mIHBy
+b2Nlc3MhPSJ1bmRlZmluZWQiJiZPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmNhbGwocHJvY2Vzcyk9
+PSJbb2JqZWN0IHByb2Nlc3NdIiYmcHJvY2Vzcy5wbGF0Zm9ybT09IndpbjMyIn0pCnMoJCwibWYiLCJ6
+NCIsZnVuY3Rpb24oKXtyZXR1cm4gUC5udSgiXltcXC1cXC4wLTlBLVpfYS16fl0qJCIpfSkKcygkLCJP
+USIsInZaIixmdW5jdGlvbigpe3JldHVybiBQLktOKCl9KQpzKCQsIlNDIiwiQU4iLGZ1bmN0aW9uKCl7
+cmV0dXJuIFAudE0oWyJBIiwiQUJCUiIsIkFDUk9OWU0iLCJBRERSRVNTIiwiQVJFQSIsIkFSVElDTEUi
+LCJBU0lERSIsIkFVRElPIiwiQiIsIkJESSIsIkJETyIsIkJJRyIsIkJMT0NLUVVPVEUiLCJCUiIsIkJV
+VFRPTiIsIkNBTlZBUyIsIkNBUFRJT04iLCJDRU5URVIiLCJDSVRFIiwiQ09ERSIsIkNPTCIsIkNPTEdS
+T1VQIiwiQ09NTUFORCIsIkRBVEEiLCJEQVRBTElTVCIsIkREIiwiREVMIiwiREVUQUlMUyIsIkRGTiIs
+IkRJUiIsIkRJViIsIkRMIiwiRFQiLCJFTSIsIkZJRUxEU0VUIiwiRklHQ0FQVElPTiIsIkZJR1VSRSIs
+IkZPTlQiLCJGT09URVIiLCJGT1JNIiwiSDEiLCJIMiIsIkgzIiwiSDQiLCJINSIsIkg2IiwiSEVBREVS
+IiwiSEdST1VQIiwiSFIiLCJJIiwiSUZSQU1FIiwiSU1HIiwiSU5QVVQiLCJJTlMiLCJLQkQiLCJMQUJF
+TCIsIkxFR0VORCIsIkxJIiwiTUFQIiwiTUFSSyIsIk1FTlUiLCJNRVRFUiIsIk5BViIsIk5PQlIiLCJP
+TCIsIk9QVEdST1VQIiwiT1BUSU9OIiwiT1VUUFVUIiwiUCIsIlBSRSIsIlBST0dSRVNTIiwiUSIsIlMi
+LCJTQU1QIiwiU0VDVElPTiIsIlNFTEVDVCIsIlNNQUxMIiwiU09VUkNFIiwiU1BBTiIsIlNUUklLRSIs
+IlNUUk9ORyIsIlNVQiIsIlNVTU1BUlkiLCJTVVAiLCJUQUJMRSIsIlRCT0RZIiwiVEQiLCJURVhUQVJF
+QSIsIlRGT09UIiwiVEgiLCJUSEVBRCIsIlRJTUUiLCJUUiIsIlRSQUNLIiwiVFQiLCJVIiwiVUwiLCJW
+QVIiLCJWSURFTyIsIldCUiJdLHQuTil9KQpzKCQsIlg0IiwiaEciLGZ1bmN0aW9uKCl7cmV0dXJuIFAu
+bnUoIl5cXFMrJCIpfSkKcygkLCJ3TyIsIm93IixmdW5jdGlvbigpe3JldHVybiBQLk5EKHNlbGYpfSkK
+cygkLCJrdCIsIlI4IixmdW5jdGlvbigpe3JldHVybiBILllnKCJfJGRhcnRfZGFydE9iamVjdCIpfSkK
+cygkLCJmSyIsImtJIixmdW5jdGlvbigpe3JldHVybiBmdW5jdGlvbiBEYXJ0T2JqZWN0KGEpe3RoaXMu
+bz1hfX0pCnMoJCwicXQiLCJ6QiIsZnVuY3Rpb24oKXtyZXR1cm4gbmV3IFQubVEoKX0pCnMoJCwiT2wi
+LCJVRSIsZnVuY3Rpb24oKXtyZXR1cm4gUC5oSyhDLm9sLmdtVyhXLngzKCkpLmhyZWYpLmdoWSgpLnEo
+MCwiYXV0aFRva2VuIil9KQpzKCQsImhUIiwieVAiLGZ1bmN0aW9uKCl7cmV0dXJuIFcuWnIoKS5xdWVy
+eVNlbGVjdG9yKCIuZWRpdC1saXN0IC5wYW5lbC1jb250ZW50Iil9KQpzKCQsIlc2IiwiaEwiLGZ1bmN0
+aW9uKCl7cmV0dXJuIFcuWnIoKS5xdWVyeVNlbGVjdG9yKCIuZWRpdC1wYW5lbCAucGFuZWwtY29udGVu
+dCIpfSkKcygkLCJUUiIsIkRXIixmdW5jdGlvbigpe3JldHVybiBXLlpyKCkucXVlcnlTZWxlY3Rvcigi
+Zm9vdGVyIil9KQpzKCQsIkVZIiwiZmkiLGZ1bmN0aW9uKCl7cmV0dXJuIFcuWnIoKS5xdWVyeVNlbGVj
+dG9yKCJoZWFkZXIiKX0pCnMoJCwiYkEiLCJjMCIsZnVuY3Rpb24oKXtyZXR1cm4gVy5acigpLnF1ZXJ5
+U2VsZWN0b3IoIiNtaWdyYXRlLXVuaXQtc3RhdHVzLWljb24iKX0pCnMoJCwidDAiLCJiTiIsZnVuY3Rp
+b24oKXtyZXR1cm4gVy5acigpLnF1ZXJ5U2VsZWN0b3IoIiNtaWdyYXRlLXVuaXQtc3RhdHVzLWljb24t
+bGFiZWwiKX0pCnMoJCwiYXYiLCJEOSIsZnVuY3Rpb24oKXtyZXR1cm4gVy5acigpLnF1ZXJ5U2VsZWN0
+b3IoIiN1bml0LW5hbWUiKX0pCnIoJCwiZmUiLCJLRyIsZnVuY3Rpb24oKXtyZXR1cm4gbmV3IEwuWEEo
+KX0pCnMoJCwiZW8iLCJuVSIsZnVuY3Rpb24oKXtyZXR1cm4gbmV3IE0ubEkoSC5OMCgiZnYiKS5hKCQu
+SGsoKSkpfSkKcygkLCJ5ciIsImJEIixmdW5jdGlvbigpe3JldHVybiBuZXcgRS5PRihQLm51KCIvIiks
+UC5udSgiW14vXSQiKSxQLm51KCJeLyIpKX0pCnMoJCwiTWsiLCJLayIsZnVuY3Rpb24oKXtyZXR1cm4g
+bmV3IEwuSVYoUC5udSgiWy9cXFxcXSIpLFAubnUoIlteL1xcXFxdJCIpLFAubnUoIl4oXFxcXFxcXFxb
+XlxcXFxdK1xcXFxbXlxcXFwvXSt8W2EtekEtWl06Wy9cXFxcXSkiKSxQLm51KCJeWy9cXFxcXSg/IVsv
+XFxcXF0pIikpfSkKcygkLCJhayIsIkViIixmdW5jdGlvbigpe3JldHVybiBuZXcgRi5ydShQLm51KCIv
+IiksUC5udSgiKF5bYS16QS1aXVstKy5hLXpBLVpcXGRdKjovL3xbXi9dKSQiKSxQLm51KCJbYS16QS1a
+XVstKy5hLXpBLVpcXGRdKjovL1teL10qIiksUC5udSgiXi8iKSl9KQpzKCQsImxzIiwiSGsiLGZ1bmN0
+aW9uKCl7cmV0dXJuIE8uUmgoKX0pfSkoKTsoZnVuY3Rpb24gbmF0aXZlU3VwcG9ydCgpeyFmdW5jdGlv
+bigpe3ZhciBzPWZ1bmN0aW9uKGEpe3ZhciBtPXt9Cm1bYV09MQpyZXR1cm4gT2JqZWN0LmtleXMoaHVu
+a0hlbHBlcnMuY29udmVydFRvRmFzdE9iamVjdChtKSlbMF19CnYuZ2V0SXNvbGF0ZVRhZz1mdW5jdGlv
+bihhKXtyZXR1cm4gcygiX19fZGFydF8iK2Erdi5pc29sYXRlVGFnKX0KdmFyIHI9Il9fX2RhcnRfaXNv
+bGF0ZV90YWdzXyIKdmFyIHE9T2JqZWN0W3JdfHwoT2JqZWN0W3JdPU9iamVjdC5jcmVhdGUobnVsbCkp
+CnZhciBwPSJfWnhZeFgiCmZvcih2YXIgbz0wOztvKyspe3ZhciBuPXMocCsiXyIrbysiXyIpCmlmKCEo
+biBpbiBxKSl7cVtuXT0xCnYuaXNvbGF0ZVRhZz1uCmJyZWFrfX12LmRpc3BhdGNoUHJvcGVydHlOYW1l
+PXYuZ2V0SXNvbGF0ZVRhZygiZGlzcGF0Y2hfcmVjb3JkIil9KCkKaHVua0hlbHBlcnMuc2V0T3JVcGRh
+dGVJbnRlcmNlcHRvcnNCeVRhZyh7RE9NRXJyb3I6Si5HdixNZWRpYUVycm9yOkouR3YsTmF2aWdhdG9y
+OkouR3YsTmF2aWdhdG9yQ29uY3VycmVudEhhcmR3YXJlOkouR3YsTmF2aWdhdG9yVXNlck1lZGlhRXJy
+b3I6Si5HdixPdmVyY29uc3RyYWluZWRFcnJvcjpKLkd2LFBvc2l0aW9uRXJyb3I6Si5HdixHZW9sb2Nh
+dGlvblBvc2l0aW9uRXJyb3I6Si5HdixSYW5nZTpKLkd2LFNRTEVycm9yOkouR3YsRGF0YVZpZXc6SC5F
+VCxBcnJheUJ1ZmZlclZpZXc6SC5FVCxGbG9hdDMyQXJyYXk6SC5EZyxGbG9hdDY0QXJyYXk6SC5EZyxJ
+bnQxNkFycmF5OkgueGosSW50MzJBcnJheTpILmRFLEludDhBcnJheTpILlpBLFVpbnQxNkFycmF5Okgu
+ZFQsVWludDMyQXJyYXk6SC5QcSxVaW50OENsYW1wZWRBcnJheTpILmVFLENhbnZhc1BpeGVsQXJyYXk6
+SC5lRSxVaW50OEFycmF5OkguVjYsSFRNTEF1ZGlvRWxlbWVudDpXLnFFLEhUTUxCUkVsZW1lbnQ6Vy5x
+RSxIVE1MQnV0dG9uRWxlbWVudDpXLnFFLEhUTUxDYW52YXNFbGVtZW50OlcucUUsSFRNTENvbnRlbnRF
+bGVtZW50OlcucUUsSFRNTERMaXN0RWxlbWVudDpXLnFFLEhUTUxEYXRhRWxlbWVudDpXLnFFLEhUTUxE
+YXRhTGlzdEVsZW1lbnQ6Vy5xRSxIVE1MRGV0YWlsc0VsZW1lbnQ6Vy5xRSxIVE1MRGlhbG9nRWxlbWVu
+dDpXLnFFLEhUTUxEaXZFbGVtZW50OlcucUUsSFRNTEVtYmVkRWxlbWVudDpXLnFFLEhUTUxGaWVsZFNl
+dEVsZW1lbnQ6Vy5xRSxIVE1MSFJFbGVtZW50OlcucUUsSFRNTEhlYWRFbGVtZW50OlcucUUsSFRNTEhl
+YWRpbmdFbGVtZW50OlcucUUsSFRNTEh0bWxFbGVtZW50OlcucUUsSFRNTElGcmFtZUVsZW1lbnQ6Vy5x
+RSxIVE1MSW1hZ2VFbGVtZW50OlcucUUsSFRNTElucHV0RWxlbWVudDpXLnFFLEhUTUxMSUVsZW1lbnQ6
+Vy5xRSxIVE1MTGFiZWxFbGVtZW50OlcucUUsSFRNTExlZ2VuZEVsZW1lbnQ6Vy5xRSxIVE1MTGlua0Vs
+ZW1lbnQ6Vy5xRSxIVE1MTWFwRWxlbWVudDpXLnFFLEhUTUxNZWRpYUVsZW1lbnQ6Vy5xRSxIVE1MTWVu
+dUVsZW1lbnQ6Vy5xRSxIVE1MTWV0YUVsZW1lbnQ6Vy5xRSxIVE1MTWV0ZXJFbGVtZW50OlcucUUsSFRN
+TE1vZEVsZW1lbnQ6Vy5xRSxIVE1MT0xpc3RFbGVtZW50OlcucUUsSFRNTE9iamVjdEVsZW1lbnQ6Vy5x
+RSxIVE1MT3B0R3JvdXBFbGVtZW50OlcucUUsSFRNTE9wdGlvbkVsZW1lbnQ6Vy5xRSxIVE1MT3V0cHV0
+RWxlbWVudDpXLnFFLEhUTUxQYXJhbUVsZW1lbnQ6Vy5xRSxIVE1MUGljdHVyZUVsZW1lbnQ6Vy5xRSxI
+VE1MUHJlRWxlbWVudDpXLnFFLEhUTUxQcm9ncmVzc0VsZW1lbnQ6Vy5xRSxIVE1MUXVvdGVFbGVtZW50
+OlcucUUsSFRNTFNjcmlwdEVsZW1lbnQ6Vy5xRSxIVE1MU2hhZG93RWxlbWVudDpXLnFFLEhUTUxTbG90
+RWxlbWVudDpXLnFFLEhUTUxTb3VyY2VFbGVtZW50OlcucUUsSFRNTFNwYW5FbGVtZW50OlcucUUsSFRN
+TFN0eWxlRWxlbWVudDpXLnFFLEhUTUxUYWJsZUNhcHRpb25FbGVtZW50OlcucUUsSFRNTFRhYmxlQ2Vs
+bEVsZW1lbnQ6Vy5xRSxIVE1MVGFibGVEYXRhQ2VsbEVsZW1lbnQ6Vy5xRSxIVE1MVGFibGVIZWFkZXJD
+ZWxsRWxlbWVudDpXLnFFLEhUTUxUYWJsZUNvbEVsZW1lbnQ6Vy5xRSxIVE1MVGV4dEFyZWFFbGVtZW50
+OlcucUUsSFRNTFRpbWVFbGVtZW50OlcucUUsSFRNTFRpdGxlRWxlbWVudDpXLnFFLEhUTUxUcmFja0Vs
+ZW1lbnQ6Vy5xRSxIVE1MVUxpc3RFbGVtZW50OlcucUUsSFRNTFVua25vd25FbGVtZW50OlcucUUsSFRN
+TFZpZGVvRWxlbWVudDpXLnFFLEhUTUxEaXJlY3RvcnlFbGVtZW50OlcucUUsSFRNTEZvbnRFbGVtZW50
+OlcucUUsSFRNTEZyYW1lRWxlbWVudDpXLnFFLEhUTUxGcmFtZVNldEVsZW1lbnQ6Vy5xRSxIVE1MTWFy
+cXVlZUVsZW1lbnQ6Vy5xRSxIVE1MRWxlbWVudDpXLnFFLEhUTUxBbmNob3JFbGVtZW50OlcuR2gsSFRN
+TEFyZWFFbGVtZW50OlcuZlksSFRNTEJhc2VFbGVtZW50OlcuclosQmxvYjpXLkF6LEhUTUxCb2R5RWxl
+bWVudDpXLlFQLENEQVRBU2VjdGlvbjpXLm54LENoYXJhY3RlckRhdGE6Vy5ueCxDb21tZW50Olcubngs
+UHJvY2Vzc2luZ0luc3RydWN0aW9uOlcubngsVGV4dDpXLm54LENTU1N0eWxlRGVjbGFyYXRpb246Vy5v
+SixNU1N0eWxlQ1NTUHJvcGVydGllczpXLm9KLENTUzJQcm9wZXJ0aWVzOlcub0osWE1MRG9jdW1lbnQ6
+Vy5RRixEb2N1bWVudDpXLlFGLERPTUV4Y2VwdGlvbjpXLk5oLERPTUltcGxlbWVudGF0aW9uOlcuYWUs
+RE9NUmVjdFJlYWRPbmx5OlcuSUIsRE9NVG9rZW5MaXN0OlcubjcsRWxlbWVudDpXLmN2LEFib3J0UGF5
+bWVudEV2ZW50OlcuZWEsQW5pbWF0aW9uRXZlbnQ6Vy5lYSxBbmltYXRpb25QbGF5YmFja0V2ZW50Olcu
+ZWEsQXBwbGljYXRpb25DYWNoZUVycm9yRXZlbnQ6Vy5lYSxCYWNrZ3JvdW5kRmV0Y2hDbGlja0V2ZW50
+OlcuZWEsQmFja2dyb3VuZEZldGNoRXZlbnQ6Vy5lYSxCYWNrZ3JvdW5kRmV0Y2hGYWlsRXZlbnQ6Vy5l
+YSxCYWNrZ3JvdW5kRmV0Y2hlZEV2ZW50OlcuZWEsQmVmb3JlSW5zdGFsbFByb21wdEV2ZW50OlcuZWEs
+QmVmb3JlVW5sb2FkRXZlbnQ6Vy5lYSxCbG9iRXZlbnQ6Vy5lYSxDYW5NYWtlUGF5bWVudEV2ZW50Olcu
+ZWEsQ2xpcGJvYXJkRXZlbnQ6Vy5lYSxDbG9zZUV2ZW50OlcuZWEsQ3VzdG9tRXZlbnQ6Vy5lYSxEZXZp
+Y2VNb3Rpb25FdmVudDpXLmVhLERldmljZU9yaWVudGF0aW9uRXZlbnQ6Vy5lYSxFcnJvckV2ZW50Olcu
+ZWEsRXh0ZW5kYWJsZUV2ZW50OlcuZWEsRXh0ZW5kYWJsZU1lc3NhZ2VFdmVudDpXLmVhLEZldGNoRXZl
+bnQ6Vy5lYSxGb250RmFjZVNldExvYWRFdmVudDpXLmVhLEZvcmVpZ25GZXRjaEV2ZW50OlcuZWEsR2Ft
+ZXBhZEV2ZW50OlcuZWEsSGFzaENoYW5nZUV2ZW50OlcuZWEsSW5zdGFsbEV2ZW50OlcuZWEsTWVkaWFF
+bmNyeXB0ZWRFdmVudDpXLmVhLE1lZGlhS2V5TWVzc2FnZUV2ZW50OlcuZWEsTWVkaWFRdWVyeUxpc3RF
+dmVudDpXLmVhLE1lZGlhU3RyZWFtRXZlbnQ6Vy5lYSxNZWRpYVN0cmVhbVRyYWNrRXZlbnQ6Vy5lYSxN
+ZXNzYWdlRXZlbnQ6Vy5lYSxNSURJQ29ubmVjdGlvbkV2ZW50OlcuZWEsTUlESU1lc3NhZ2VFdmVudDpX
+LmVhLE11dGF0aW9uRXZlbnQ6Vy5lYSxOb3RpZmljYXRpb25FdmVudDpXLmVhLFBhZ2VUcmFuc2l0aW9u
+RXZlbnQ6Vy5lYSxQYXltZW50UmVxdWVzdEV2ZW50OlcuZWEsUGF5bWVudFJlcXVlc3RVcGRhdGVFdmVu
+dDpXLmVhLFBvcFN0YXRlRXZlbnQ6Vy5lYSxQcmVzZW50YXRpb25Db25uZWN0aW9uQXZhaWxhYmxlRXZl
+bnQ6Vy5lYSxQcmVzZW50YXRpb25Db25uZWN0aW9uQ2xvc2VFdmVudDpXLmVhLFByb21pc2VSZWplY3Rp
+b25FdmVudDpXLmVhLFB1c2hFdmVudDpXLmVhLFJUQ0RhdGFDaGFubmVsRXZlbnQ6Vy5lYSxSVENEVE1G
+VG9uZUNoYW5nZUV2ZW50OlcuZWEsUlRDUGVlckNvbm5lY3Rpb25JY2VFdmVudDpXLmVhLFJUQ1RyYWNr
+RXZlbnQ6Vy5lYSxTZWN1cml0eVBvbGljeVZpb2xhdGlvbkV2ZW50OlcuZWEsU2Vuc29yRXJyb3JFdmVu
+dDpXLmVhLFNwZWVjaFJlY29nbml0aW9uRXJyb3I6Vy5lYSxTcGVlY2hSZWNvZ25pdGlvbkV2ZW50Olcu
+ZWEsU3BlZWNoU3ludGhlc2lzRXZlbnQ6Vy5lYSxTdG9yYWdlRXZlbnQ6Vy5lYSxTeW5jRXZlbnQ6Vy5l
+YSxUcmFja0V2ZW50OlcuZWEsVHJhbnNpdGlvbkV2ZW50OlcuZWEsV2ViS2l0VHJhbnNpdGlvbkV2ZW50
+OlcuZWEsVlJEZXZpY2VFdmVudDpXLmVhLFZSRGlzcGxheUV2ZW50OlcuZWEsVlJTZXNzaW9uRXZlbnQ6
+Vy5lYSxNb2pvSW50ZXJmYWNlUmVxdWVzdEV2ZW50OlcuZWEsVVNCQ29ubmVjdGlvbkV2ZW50OlcuZWEs
+SURCVmVyc2lvbkNoYW5nZUV2ZW50OlcuZWEsQXVkaW9Qcm9jZXNzaW5nRXZlbnQ6Vy5lYSxPZmZsaW5l
+QXVkaW9Db21wbGV0aW9uRXZlbnQ6Vy5lYSxXZWJHTENvbnRleHRFdmVudDpXLmVhLEV2ZW50OlcuZWEs
+SW5wdXRFdmVudDpXLmVhLFN1Ym1pdEV2ZW50OlcuZWEsRXZlbnRUYXJnZXQ6Vy5EMCxGaWxlOlcuaEgs
+SFRNTEZvcm1FbGVtZW50OlcuaDQsSGlzdG9yeTpXLmJyLEhUTUxEb2N1bWVudDpXLlZiLFhNTEh0dHBS
+ZXF1ZXN0OlcuZkosWE1MSHR0cFJlcXVlc3RFdmVudFRhcmdldDpXLndhLEltYWdlRGF0YTpXLlNnLExv
+Y2F0aW9uOlcudTgsTW91c2VFdmVudDpXLkFqLERyYWdFdmVudDpXLkFqLFBvaW50ZXJFdmVudDpXLkFq
+LFdoZWVsRXZlbnQ6Vy5BaixEb2N1bWVudEZyYWdtZW50OlcuS1YsU2hhZG93Um9vdDpXLktWLERvY3Vt
+ZW50VHlwZTpXLktWLE5vZGU6Vy5LVixOb2RlTGlzdDpXLkJILFJhZGlvTm9kZUxpc3Q6Vy5CSCxIVE1M
+UGFyYWdyYXBoRWxlbWVudDpXLlNOLFByb2dyZXNzRXZlbnQ6Vy5ldyxSZXNvdXJjZVByb2dyZXNzRXZl
+bnQ6Vy5ldyxIVE1MU2VsZWN0RWxlbWVudDpXLmxwLEhUTUxUYWJsZUVsZW1lbnQ6Vy5UYixIVE1MVGFi
+bGVSb3dFbGVtZW50OlcuSXYsSFRNTFRhYmxlU2VjdGlvbkVsZW1lbnQ6Vy5XUCxIVE1MVGVtcGxhdGVF
+bGVtZW50OlcueVksQ29tcG9zaXRpb25FdmVudDpXLnc2LEZvY3VzRXZlbnQ6Vy53NixLZXlib2FyZEV2
+ZW50OlcudzYsVGV4dEV2ZW50OlcudzYsVG91Y2hFdmVudDpXLnc2LFVJRXZlbnQ6Vy53NixXaW5kb3c6
+Vy5LNSxET01XaW5kb3c6Vy5LNSxEZWRpY2F0ZWRXb3JrZXJHbG9iYWxTY29wZTpXLkNtLFNlcnZpY2VX
+b3JrZXJHbG9iYWxTY29wZTpXLkNtLFNoYXJlZFdvcmtlckdsb2JhbFNjb3BlOlcuQ20sV29ya2VyR2xv
+YmFsU2NvcGU6Vy5DbSxBdHRyOlcuQ1EsQ2xpZW50UmVjdDpXLnc0LERPTVJlY3Q6Vy53NCxOYW1lZE5v
+ZGVNYXA6Vy5yaCxNb3pOYW1lZEF0dHJNYXA6Vy5yaCxJREJLZXlSYW5nZTpQLmhGLFNWR1NjcmlwdEVs
+ZW1lbnQ6UC5uZCxTVkdBRWxlbWVudDpQLmhpLFNWR0FuaW1hdGVFbGVtZW50OlAuaGksU1ZHQW5pbWF0
+ZU1vdGlvbkVsZW1lbnQ6UC5oaSxTVkdBbmltYXRlVHJhbnNmb3JtRWxlbWVudDpQLmhpLFNWR0FuaW1h
+dGlvbkVsZW1lbnQ6UC5oaSxTVkdDaXJjbGVFbGVtZW50OlAuaGksU1ZHQ2xpcFBhdGhFbGVtZW50OlAu
+aGksU1ZHRGVmc0VsZW1lbnQ6UC5oaSxTVkdEZXNjRWxlbWVudDpQLmhpLFNWR0Rpc2NhcmRFbGVtZW50
+OlAuaGksU1ZHRWxsaXBzZUVsZW1lbnQ6UC5oaSxTVkdGRUJsZW5kRWxlbWVudDpQLmhpLFNWR0ZFQ29s
+b3JNYXRyaXhFbGVtZW50OlAuaGksU1ZHRkVDb21wb25lbnRUcmFuc2ZlckVsZW1lbnQ6UC5oaSxTVkdG
+RUNvbXBvc2l0ZUVsZW1lbnQ6UC5oaSxTVkdGRUNvbnZvbHZlTWF0cml4RWxlbWVudDpQLmhpLFNWR0ZF
+RGlmZnVzZUxpZ2h0aW5nRWxlbWVudDpQLmhpLFNWR0ZFRGlzcGxhY2VtZW50TWFwRWxlbWVudDpQLmhp
+LFNWR0ZFRGlzdGFudExpZ2h0RWxlbWVudDpQLmhpLFNWR0ZFRmxvb2RFbGVtZW50OlAuaGksU1ZHRkVG
+dW5jQUVsZW1lbnQ6UC5oaSxTVkdGRUZ1bmNCRWxlbWVudDpQLmhpLFNWR0ZFRnVuY0dFbGVtZW50OlAu
+aGksU1ZHRkVGdW5jUkVsZW1lbnQ6UC5oaSxTVkdGRUdhdXNzaWFuQmx1ckVsZW1lbnQ6UC5oaSxTVkdG
+RUltYWdlRWxlbWVudDpQLmhpLFNWR0ZFTWVyZ2VFbGVtZW50OlAuaGksU1ZHRkVNZXJnZU5vZGVFbGVt
+ZW50OlAuaGksU1ZHRkVNb3JwaG9sb2d5RWxlbWVudDpQLmhpLFNWR0ZFT2Zmc2V0RWxlbWVudDpQLmhp
+LFNWR0ZFUG9pbnRMaWdodEVsZW1lbnQ6UC5oaSxTVkdGRVNwZWN1bGFyTGlnaHRpbmdFbGVtZW50OlAu
+aGksU1ZHRkVTcG90TGlnaHRFbGVtZW50OlAuaGksU1ZHRkVUaWxlRWxlbWVudDpQLmhpLFNWR0ZFVHVy
+YnVsZW5jZUVsZW1lbnQ6UC5oaSxTVkdGaWx0ZXJFbGVtZW50OlAuaGksU1ZHRm9yZWlnbk9iamVjdEVs
+ZW1lbnQ6UC5oaSxTVkdHRWxlbWVudDpQLmhpLFNWR0dlb21ldHJ5RWxlbWVudDpQLmhpLFNWR0dyYXBo
+aWNzRWxlbWVudDpQLmhpLFNWR0ltYWdlRWxlbWVudDpQLmhpLFNWR0xpbmVFbGVtZW50OlAuaGksU1ZH
+TGluZWFyR3JhZGllbnRFbGVtZW50OlAuaGksU1ZHTWFya2VyRWxlbWVudDpQLmhpLFNWR01hc2tFbGVt
+ZW50OlAuaGksU1ZHTWV0YWRhdGFFbGVtZW50OlAuaGksU1ZHUGF0aEVsZW1lbnQ6UC5oaSxTVkdQYXR0
+ZXJuRWxlbWVudDpQLmhpLFNWR1BvbHlnb25FbGVtZW50OlAuaGksU1ZHUG9seWxpbmVFbGVtZW50OlAu
+aGksU1ZHUmFkaWFsR3JhZGllbnRFbGVtZW50OlAuaGksU1ZHUmVjdEVsZW1lbnQ6UC5oaSxTVkdTZXRF
+bGVtZW50OlAuaGksU1ZHU3RvcEVsZW1lbnQ6UC5oaSxTVkdTdHlsZUVsZW1lbnQ6UC5oaSxTVkdTVkdF
+bGVtZW50OlAuaGksU1ZHU3dpdGNoRWxlbWVudDpQLmhpLFNWR1N5bWJvbEVsZW1lbnQ6UC5oaSxTVkdU
+U3BhbkVsZW1lbnQ6UC5oaSxTVkdUZXh0Q29udGVudEVsZW1lbnQ6UC5oaSxTVkdUZXh0RWxlbWVudDpQ
+LmhpLFNWR1RleHRQYXRoRWxlbWVudDpQLmhpLFNWR1RleHRQb3NpdGlvbmluZ0VsZW1lbnQ6UC5oaSxT
+VkdUaXRsZUVsZW1lbnQ6UC5oaSxTVkdVc2VFbGVtZW50OlAuaGksU1ZHVmlld0VsZW1lbnQ6UC5oaSxT
+VkdHcmFkaWVudEVsZW1lbnQ6UC5oaSxTVkdDb21wb25lbnRUcmFuc2ZlckZ1bmN0aW9uRWxlbWVudDpQ
+LmhpLFNWR0ZFRHJvcFNoYWRvd0VsZW1lbnQ6UC5oaSxTVkdNUGF0aEVsZW1lbnQ6UC5oaSxTVkdFbGVt
+ZW50OlAuaGl9KQpodW5rSGVscGVycy5zZXRPclVwZGF0ZUxlYWZUYWdzKHtET01FcnJvcjp0cnVlLE1l
+ZGlhRXJyb3I6dHJ1ZSxOYXZpZ2F0b3I6dHJ1ZSxOYXZpZ2F0b3JDb25jdXJyZW50SGFyZHdhcmU6dHJ1
+ZSxOYXZpZ2F0b3JVc2VyTWVkaWFFcnJvcjp0cnVlLE92ZXJjb25zdHJhaW5lZEVycm9yOnRydWUsUG9z
+aXRpb25FcnJvcjp0cnVlLEdlb2xvY2F0aW9uUG9zaXRpb25FcnJvcjp0cnVlLFJhbmdlOnRydWUsU1FM
+RXJyb3I6dHJ1ZSxEYXRhVmlldzp0cnVlLEFycmF5QnVmZmVyVmlldzpmYWxzZSxGbG9hdDMyQXJyYXk6
+dHJ1ZSxGbG9hdDY0QXJyYXk6dHJ1ZSxJbnQxNkFycmF5OnRydWUsSW50MzJBcnJheTp0cnVlLEludDhB
+cnJheTp0cnVlLFVpbnQxNkFycmF5OnRydWUsVWludDMyQXJyYXk6dHJ1ZSxVaW50OENsYW1wZWRBcnJh
+eTp0cnVlLENhbnZhc1BpeGVsQXJyYXk6dHJ1ZSxVaW50OEFycmF5OmZhbHNlLEhUTUxBdWRpb0VsZW1l
+bnQ6dHJ1ZSxIVE1MQlJFbGVtZW50OnRydWUsSFRNTEJ1dHRvbkVsZW1lbnQ6dHJ1ZSxIVE1MQ2FudmFz
+RWxlbWVudDp0cnVlLEhUTUxDb250ZW50RWxlbWVudDp0cnVlLEhUTUxETGlzdEVsZW1lbnQ6dHJ1ZSxI
+VE1MRGF0YUVsZW1lbnQ6dHJ1ZSxIVE1MRGF0YUxpc3RFbGVtZW50OnRydWUsSFRNTERldGFpbHNFbGVt
+ZW50OnRydWUsSFRNTERpYWxvZ0VsZW1lbnQ6dHJ1ZSxIVE1MRGl2RWxlbWVudDp0cnVlLEhUTUxFbWJl
+ZEVsZW1lbnQ6dHJ1ZSxIVE1MRmllbGRTZXRFbGVtZW50OnRydWUsSFRNTEhSRWxlbWVudDp0cnVlLEhU
+TUxIZWFkRWxlbWVudDp0cnVlLEhUTUxIZWFkaW5nRWxlbWVudDp0cnVlLEhUTUxIdG1sRWxlbWVudDp0
+cnVlLEhUTUxJRnJhbWVFbGVtZW50OnRydWUsSFRNTEltYWdlRWxlbWVudDp0cnVlLEhUTUxJbnB1dEVs
+ZW1lbnQ6dHJ1ZSxIVE1MTElFbGVtZW50OnRydWUsSFRNTExhYmVsRWxlbWVudDp0cnVlLEhUTUxMZWdl
+bmRFbGVtZW50OnRydWUsSFRNTExpbmtFbGVtZW50OnRydWUsSFRNTE1hcEVsZW1lbnQ6dHJ1ZSxIVE1M
+TWVkaWFFbGVtZW50OnRydWUsSFRNTE1lbnVFbGVtZW50OnRydWUsSFRNTE1ldGFFbGVtZW50OnRydWUs
+SFRNTE1ldGVyRWxlbWVudDp0cnVlLEhUTUxNb2RFbGVtZW50OnRydWUsSFRNTE9MaXN0RWxlbWVudDp0
+cnVlLEhUTUxPYmplY3RFbGVtZW50OnRydWUsSFRNTE9wdEdyb3VwRWxlbWVudDp0cnVlLEhUTUxPcHRp
+b25FbGVtZW50OnRydWUsSFRNTE91dHB1dEVsZW1lbnQ6dHJ1ZSxIVE1MUGFyYW1FbGVtZW50OnRydWUs
+SFRNTFBpY3R1cmVFbGVtZW50OnRydWUsSFRNTFByZUVsZW1lbnQ6dHJ1ZSxIVE1MUHJvZ3Jlc3NFbGVt
+ZW50OnRydWUsSFRNTFF1b3RlRWxlbWVudDp0cnVlLEhUTUxTY3JpcHRFbGVtZW50OnRydWUsSFRNTFNo
+YWRvd0VsZW1lbnQ6dHJ1ZSxIVE1MU2xvdEVsZW1lbnQ6dHJ1ZSxIVE1MU291cmNlRWxlbWVudDp0cnVl
+LEhUTUxTcGFuRWxlbWVudDp0cnVlLEhUTUxTdHlsZUVsZW1lbnQ6dHJ1ZSxIVE1MVGFibGVDYXB0aW9u
+RWxlbWVudDp0cnVlLEhUTUxUYWJsZUNlbGxFbGVtZW50OnRydWUsSFRNTFRhYmxlRGF0YUNlbGxFbGVt
+ZW50OnRydWUsSFRNTFRhYmxlSGVhZGVyQ2VsbEVsZW1lbnQ6dHJ1ZSxIVE1MVGFibGVDb2xFbGVtZW50
+OnRydWUsSFRNTFRleHRBcmVhRWxlbWVudDp0cnVlLEhUTUxUaW1lRWxlbWVudDp0cnVlLEhUTUxUaXRs
+ZUVsZW1lbnQ6dHJ1ZSxIVE1MVHJhY2tFbGVtZW50OnRydWUsSFRNTFVMaXN0RWxlbWVudDp0cnVlLEhU
+TUxVbmtub3duRWxlbWVudDp0cnVlLEhUTUxWaWRlb0VsZW1lbnQ6dHJ1ZSxIVE1MRGlyZWN0b3J5RWxl
+bWVudDp0cnVlLEhUTUxGb250RWxlbWVudDp0cnVlLEhUTUxGcmFtZUVsZW1lbnQ6dHJ1ZSxIVE1MRnJh
+bWVTZXRFbGVtZW50OnRydWUsSFRNTE1hcnF1ZWVFbGVtZW50OnRydWUsSFRNTEVsZW1lbnQ6ZmFsc2Us
+SFRNTEFuY2hvckVsZW1lbnQ6dHJ1ZSxIVE1MQXJlYUVsZW1lbnQ6dHJ1ZSxIVE1MQmFzZUVsZW1lbnQ6
+dHJ1ZSxCbG9iOmZhbHNlLEhUTUxCb2R5RWxlbWVudDp0cnVlLENEQVRBU2VjdGlvbjp0cnVlLENoYXJh
+Y3RlckRhdGE6dHJ1ZSxDb21tZW50OnRydWUsUHJvY2Vzc2luZ0luc3RydWN0aW9uOnRydWUsVGV4dDp0
+cnVlLENTU1N0eWxlRGVjbGFyYXRpb246dHJ1ZSxNU1N0eWxlQ1NTUHJvcGVydGllczp0cnVlLENTUzJQ
+cm9wZXJ0aWVzOnRydWUsWE1MRG9jdW1lbnQ6dHJ1ZSxEb2N1bWVudDpmYWxzZSxET01FeGNlcHRpb246
+dHJ1ZSxET01JbXBsZW1lbnRhdGlvbjp0cnVlLERPTVJlY3RSZWFkT25seTpmYWxzZSxET01Ub2tlbkxp
+c3Q6dHJ1ZSxFbGVtZW50OmZhbHNlLEFib3J0UGF5bWVudEV2ZW50OnRydWUsQW5pbWF0aW9uRXZlbnQ6
+dHJ1ZSxBbmltYXRpb25QbGF5YmFja0V2ZW50OnRydWUsQXBwbGljYXRpb25DYWNoZUVycm9yRXZlbnQ6
+dHJ1ZSxCYWNrZ3JvdW5kRmV0Y2hDbGlja0V2ZW50OnRydWUsQmFja2dyb3VuZEZldGNoRXZlbnQ6dHJ1
+ZSxCYWNrZ3JvdW5kRmV0Y2hGYWlsRXZlbnQ6dHJ1ZSxCYWNrZ3JvdW5kRmV0Y2hlZEV2ZW50OnRydWUs
+QmVmb3JlSW5zdGFsbFByb21wdEV2ZW50OnRydWUsQmVmb3JlVW5sb2FkRXZlbnQ6dHJ1ZSxCbG9iRXZl
+bnQ6dHJ1ZSxDYW5NYWtlUGF5bWVudEV2ZW50OnRydWUsQ2xpcGJvYXJkRXZlbnQ6dHJ1ZSxDbG9zZUV2
+ZW50OnRydWUsQ3VzdG9tRXZlbnQ6dHJ1ZSxEZXZpY2VNb3Rpb25FdmVudDp0cnVlLERldmljZU9yaWVu
+dGF0aW9uRXZlbnQ6dHJ1ZSxFcnJvckV2ZW50OnRydWUsRXh0ZW5kYWJsZUV2ZW50OnRydWUsRXh0ZW5k
+YWJsZU1lc3NhZ2VFdmVudDp0cnVlLEZldGNoRXZlbnQ6dHJ1ZSxGb250RmFjZVNldExvYWRFdmVudDp0
+cnVlLEZvcmVpZ25GZXRjaEV2ZW50OnRydWUsR2FtZXBhZEV2ZW50OnRydWUsSGFzaENoYW5nZUV2ZW50
+OnRydWUsSW5zdGFsbEV2ZW50OnRydWUsTWVkaWFFbmNyeXB0ZWRFdmVudDp0cnVlLE1lZGlhS2V5TWVz
+c2FnZUV2ZW50OnRydWUsTWVkaWFRdWVyeUxpc3RFdmVudDp0cnVlLE1lZGlhU3RyZWFtRXZlbnQ6dHJ1
+ZSxNZWRpYVN0cmVhbVRyYWNrRXZlbnQ6dHJ1ZSxNZXNzYWdlRXZlbnQ6dHJ1ZSxNSURJQ29ubmVjdGlv
+bkV2ZW50OnRydWUsTUlESU1lc3NhZ2VFdmVudDp0cnVlLE11dGF0aW9uRXZlbnQ6dHJ1ZSxOb3RpZmlj
+YXRpb25FdmVudDp0cnVlLFBhZ2VUcmFuc2l0aW9uRXZlbnQ6dHJ1ZSxQYXltZW50UmVxdWVzdEV2ZW50
+OnRydWUsUGF5bWVudFJlcXVlc3RVcGRhdGVFdmVudDp0cnVlLFBvcFN0YXRlRXZlbnQ6dHJ1ZSxQcmVz
+ZW50YXRpb25Db25uZWN0aW9uQXZhaWxhYmxlRXZlbnQ6dHJ1ZSxQcmVzZW50YXRpb25Db25uZWN0aW9u
+Q2xvc2VFdmVudDp0cnVlLFByb21pc2VSZWplY3Rpb25FdmVudDp0cnVlLFB1c2hFdmVudDp0cnVlLFJU
+Q0RhdGFDaGFubmVsRXZlbnQ6dHJ1ZSxSVENEVE1GVG9uZUNoYW5nZUV2ZW50OnRydWUsUlRDUGVlckNv
+bm5lY3Rpb25JY2VFdmVudDp0cnVlLFJUQ1RyYWNrRXZlbnQ6dHJ1ZSxTZWN1cml0eVBvbGljeVZpb2xh
+dGlvbkV2ZW50OnRydWUsU2Vuc29yRXJyb3JFdmVudDp0cnVlLFNwZWVjaFJlY29nbml0aW9uRXJyb3I6
+dHJ1ZSxTcGVlY2hSZWNvZ25pdGlvbkV2ZW50OnRydWUsU3BlZWNoU3ludGhlc2lzRXZlbnQ6dHJ1ZSxT
+dG9yYWdlRXZlbnQ6dHJ1ZSxTeW5jRXZlbnQ6dHJ1ZSxUcmFja0V2ZW50OnRydWUsVHJhbnNpdGlvbkV2
+ZW50OnRydWUsV2ViS2l0VHJhbnNpdGlvbkV2ZW50OnRydWUsVlJEZXZpY2VFdmVudDp0cnVlLFZSRGlz
+cGxheUV2ZW50OnRydWUsVlJTZXNzaW9uRXZlbnQ6dHJ1ZSxNb2pvSW50ZXJmYWNlUmVxdWVzdEV2ZW50
+OnRydWUsVVNCQ29ubmVjdGlvbkV2ZW50OnRydWUsSURCVmVyc2lvbkNoYW5nZUV2ZW50OnRydWUsQXVk
+aW9Qcm9jZXNzaW5nRXZlbnQ6dHJ1ZSxPZmZsaW5lQXVkaW9Db21wbGV0aW9uRXZlbnQ6dHJ1ZSxXZWJH
+TENvbnRleHRFdmVudDp0cnVlLEV2ZW50OmZhbHNlLElucHV0RXZlbnQ6ZmFsc2UsU3VibWl0RXZlbnQ6
+ZmFsc2UsRXZlbnRUYXJnZXQ6ZmFsc2UsRmlsZTp0cnVlLEhUTUxGb3JtRWxlbWVudDp0cnVlLEhpc3Rv
+cnk6dHJ1ZSxIVE1MRG9jdW1lbnQ6dHJ1ZSxYTUxIdHRwUmVxdWVzdDp0cnVlLFhNTEh0dHBSZXF1ZXN0
+RXZlbnRUYXJnZXQ6ZmFsc2UsSW1hZ2VEYXRhOnRydWUsTG9jYXRpb246dHJ1ZSxNb3VzZUV2ZW50OnRy
+dWUsRHJhZ0V2ZW50OnRydWUsUG9pbnRlckV2ZW50OnRydWUsV2hlZWxFdmVudDp0cnVlLERvY3VtZW50
+RnJhZ21lbnQ6dHJ1ZSxTaGFkb3dSb290OnRydWUsRG9jdW1lbnRUeXBlOnRydWUsTm9kZTpmYWxzZSxO
+b2RlTGlzdDp0cnVlLFJhZGlvTm9kZUxpc3Q6dHJ1ZSxIVE1MUGFyYWdyYXBoRWxlbWVudDp0cnVlLFBy
+b2dyZXNzRXZlbnQ6dHJ1ZSxSZXNvdXJjZVByb2dyZXNzRXZlbnQ6dHJ1ZSxIVE1MU2VsZWN0RWxlbWVu
+dDp0cnVlLEhUTUxUYWJsZUVsZW1lbnQ6dHJ1ZSxIVE1MVGFibGVSb3dFbGVtZW50OnRydWUsSFRNTFRh
+YmxlU2VjdGlvbkVsZW1lbnQ6dHJ1ZSxIVE1MVGVtcGxhdGVFbGVtZW50OnRydWUsQ29tcG9zaXRpb25F
+dmVudDp0cnVlLEZvY3VzRXZlbnQ6dHJ1ZSxLZXlib2FyZEV2ZW50OnRydWUsVGV4dEV2ZW50OnRydWUs
+VG91Y2hFdmVudDp0cnVlLFVJRXZlbnQ6ZmFsc2UsV2luZG93OnRydWUsRE9NV2luZG93OnRydWUsRGVk
+aWNhdGVkV29ya2VyR2xvYmFsU2NvcGU6dHJ1ZSxTZXJ2aWNlV29ya2VyR2xvYmFsU2NvcGU6dHJ1ZSxT
+aGFyZWRXb3JrZXJHbG9iYWxTY29wZTp0cnVlLFdvcmtlckdsb2JhbFNjb3BlOnRydWUsQXR0cjp0cnVl
+LENsaWVudFJlY3Q6dHJ1ZSxET01SZWN0OnRydWUsTmFtZWROb2RlTWFwOnRydWUsTW96TmFtZWRBdHRy
+TWFwOnRydWUsSURCS2V5UmFuZ2U6dHJ1ZSxTVkdTY3JpcHRFbGVtZW50OnRydWUsU1ZHQUVsZW1lbnQ6
+dHJ1ZSxTVkdBbmltYXRlRWxlbWVudDp0cnVlLFNWR0FuaW1hdGVNb3Rpb25FbGVtZW50OnRydWUsU1ZH
+QW5pbWF0ZVRyYW5zZm9ybUVsZW1lbnQ6dHJ1ZSxTVkdBbmltYXRpb25FbGVtZW50OnRydWUsU1ZHQ2ly
+Y2xlRWxlbWVudDp0cnVlLFNWR0NsaXBQYXRoRWxlbWVudDp0cnVlLFNWR0RlZnNFbGVtZW50OnRydWUs
+U1ZHRGVzY0VsZW1lbnQ6dHJ1ZSxTVkdEaXNjYXJkRWxlbWVudDp0cnVlLFNWR0VsbGlwc2VFbGVtZW50
+OnRydWUsU1ZHRkVCbGVuZEVsZW1lbnQ6dHJ1ZSxTVkdGRUNvbG9yTWF0cml4RWxlbWVudDp0cnVlLFNW
+R0ZFQ29tcG9uZW50VHJhbnNmZXJFbGVtZW50OnRydWUsU1ZHRkVDb21wb3NpdGVFbGVtZW50OnRydWUs
+U1ZHRkVDb252b2x2ZU1hdHJpeEVsZW1lbnQ6dHJ1ZSxTVkdGRURpZmZ1c2VMaWdodGluZ0VsZW1lbnQ6
+dHJ1ZSxTVkdGRURpc3BsYWNlbWVudE1hcEVsZW1lbnQ6dHJ1ZSxTVkdGRURpc3RhbnRMaWdodEVsZW1l
+bnQ6dHJ1ZSxTVkdGRUZsb29kRWxlbWVudDp0cnVlLFNWR0ZFRnVuY0FFbGVtZW50OnRydWUsU1ZHRkVG
+dW5jQkVsZW1lbnQ6dHJ1ZSxTVkdGRUZ1bmNHRWxlbWVudDp0cnVlLFNWR0ZFRnVuY1JFbGVtZW50OnRy
+dWUsU1ZHRkVHYXVzc2lhbkJsdXJFbGVtZW50OnRydWUsU1ZHRkVJbWFnZUVsZW1lbnQ6dHJ1ZSxTVkdG
+RU1lcmdlRWxlbWVudDp0cnVlLFNWR0ZFTWVyZ2VOb2RlRWxlbWVudDp0cnVlLFNWR0ZFTW9ycGhvbG9n
+eUVsZW1lbnQ6dHJ1ZSxTVkdGRU9mZnNldEVsZW1lbnQ6dHJ1ZSxTVkdGRVBvaW50TGlnaHRFbGVtZW50
+OnRydWUsU1ZHRkVTcGVjdWxhckxpZ2h0aW5nRWxlbWVudDp0cnVlLFNWR0ZFU3BvdExpZ2h0RWxlbWVu
+dDp0cnVlLFNWR0ZFVGlsZUVsZW1lbnQ6dHJ1ZSxTVkdGRVR1cmJ1bGVuY2VFbGVtZW50OnRydWUsU1ZH
+RmlsdGVyRWxlbWVudDp0cnVlLFNWR0ZvcmVpZ25PYmplY3RFbGVtZW50OnRydWUsU1ZHR0VsZW1lbnQ6
+dHJ1ZSxTVkdHZW9tZXRyeUVsZW1lbnQ6dHJ1ZSxTVkdHcmFwaGljc0VsZW1lbnQ6dHJ1ZSxTVkdJbWFn
+ZUVsZW1lbnQ6dHJ1ZSxTVkdMaW5lRWxlbWVudDp0cnVlLFNWR0xpbmVhckdyYWRpZW50RWxlbWVudDp0
+cnVlLFNWR01hcmtlckVsZW1lbnQ6dHJ1ZSxTVkdNYXNrRWxlbWVudDp0cnVlLFNWR01ldGFkYXRhRWxl
+bWVudDp0cnVlLFNWR1BhdGhFbGVtZW50OnRydWUsU1ZHUGF0dGVybkVsZW1lbnQ6dHJ1ZSxTVkdQb2x5
+Z29uRWxlbWVudDp0cnVlLFNWR1BvbHlsaW5lRWxlbWVudDp0cnVlLFNWR1JhZGlhbEdyYWRpZW50RWxl
+bWVudDp0cnVlLFNWR1JlY3RFbGVtZW50OnRydWUsU1ZHU2V0RWxlbWVudDp0cnVlLFNWR1N0b3BFbGVt
+ZW50OnRydWUsU1ZHU3R5bGVFbGVtZW50OnRydWUsU1ZHU1ZHRWxlbWVudDp0cnVlLFNWR1N3aXRjaEVs
+ZW1lbnQ6dHJ1ZSxTVkdTeW1ib2xFbGVtZW50OnRydWUsU1ZHVFNwYW5FbGVtZW50OnRydWUsU1ZHVGV4
+dENvbnRlbnRFbGVtZW50OnRydWUsU1ZHVGV4dEVsZW1lbnQ6dHJ1ZSxTVkdUZXh0UGF0aEVsZW1lbnQ6
+dHJ1ZSxTVkdUZXh0UG9zaXRpb25pbmdFbGVtZW50OnRydWUsU1ZHVGl0bGVFbGVtZW50OnRydWUsU1ZH
+VXNlRWxlbWVudDp0cnVlLFNWR1ZpZXdFbGVtZW50OnRydWUsU1ZHR3JhZGllbnRFbGVtZW50OnRydWUs
+U1ZHQ29tcG9uZW50VHJhbnNmZXJGdW5jdGlvbkVsZW1lbnQ6dHJ1ZSxTVkdGRURyb3BTaGFkb3dFbGVt
+ZW50OnRydWUsU1ZHTVBhdGhFbGVtZW50OnRydWUsU1ZHRWxlbWVudDpmYWxzZX0pCkguTFouJG5hdGl2
+ZVN1cGVyY2xhc3NUYWc9IkFycmF5QnVmZmVyVmlldyIKSC5SRy4kbmF0aXZlU3VwZXJjbGFzc1RhZz0i
+QXJyYXlCdWZmZXJWaWV3IgpILlZQLiRuYXRpdmVTdXBlcmNsYXNzVGFnPSJBcnJheUJ1ZmZlclZpZXci
+CkguRGcuJG5hdGl2ZVN1cGVyY2xhc3NUYWc9IkFycmF5QnVmZmVyVmlldyIKSC5XQi4kbmF0aXZlU3Vw
+ZXJjbGFzc1RhZz0iQXJyYXlCdWZmZXJWaWV3IgpILlpHLiRuYXRpdmVTdXBlcmNsYXNzVGFnPSJBcnJh
+eUJ1ZmZlclZpZXciCkguUGcuJG5hdGl2ZVN1cGVyY2xhc3NUYWc9IkFycmF5QnVmZmVyVmlldyJ9KSgp
+CmNvbnZlcnRBbGxUb0Zhc3RPYmplY3QodykKY29udmVydFRvRmFzdE9iamVjdCgkKTsoZnVuY3Rpb24o
+YSl7aWYodHlwZW9mIGRvY3VtZW50PT09InVuZGVmaW5lZCIpe2EobnVsbCkKcmV0dXJufWlmKHR5cGVv
+ZiBkb2N1bWVudC5jdXJyZW50U2NyaXB0IT0idW5kZWZpbmVkIil7YShkb2N1bWVudC5jdXJyZW50U2Ny
+aXB0KQpyZXR1cm59dmFyIHM9ZG9jdW1lbnQuc2NyaXB0cwpmdW5jdGlvbiBvbkxvYWQoYil7Zm9yKHZh
+ciBxPTA7cTxzLmxlbmd0aDsrK3Epc1txXS5yZW1vdmVFdmVudExpc3RlbmVyKCJsb2FkIixvbkxvYWQs
+ZmFsc2UpCmEoYi50YXJnZXQpfWZvcih2YXIgcj0wO3I8cy5sZW5ndGg7KytyKXNbcl0uYWRkRXZlbnRM
+aXN0ZW5lcigibG9hZCIsb25Mb2FkLGZhbHNlKX0pKGZ1bmN0aW9uKGEpe3YuY3VycmVudFNjcmlwdD1h
+CnZhciBzPUwuSXEKaWYodHlwZW9mIGRhcnRNYWluUnVubmVyPT09ImZ1bmN0aW9uIilkYXJ0TWFpblJ1
+bm5lcihzLFtdKQplbHNlIHMoW10pfSl9KSgpCi8vIyBzb3VyY2VNYXBwaW5nVVJMPW1pZ3JhdGlvbi5q
+cy5tYXAK
''';
diff --git a/pkg/nnbd_migration/lib/src/front_end/unit_link.dart b/pkg/nnbd_migration/lib/src/front_end/unit_link.dart
index bbf031b..8ab5c79 100644
--- a/pkg/nnbd_migration/lib/src/front_end/unit_link.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/unit_link.dart
@@ -6,7 +6,7 @@
/// Information about a link to a compilation unit.
class UnitLink {
- final String fullPath;
+ final String? fullPath;
final List<String> pathParts;
final int editCount;
@@ -19,7 +19,7 @@
/// the start of this migration.
final bool wasExplicitlyOptedOut;
- final UnitMigrationStatus migrationStatus;
+ final UnitMigrationStatus? migrationStatus;
/// Whether the migration status of this compilation unit can be changed in
/// the web interface.
diff --git a/pkg/nnbd_migration/lib/src/front_end/unit_renderer.dart b/pkg/nnbd_migration/lib/src/front_end/unit_renderer.dart
index 0ede8f9..c5f8ef3 100644
--- a/pkg/nnbd_migration/lib/src/front_end/unit_renderer.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/unit_renderer.dart
@@ -56,11 +56,11 @@
/// Information for a whole migration, so that libraries can reference each
/// other.
- final MigrationInfo migrationInfo;
+ final MigrationInfo? migrationInfo;
/// An object used to map the file paths of analyzed files to the file paths
/// of the HTML files used to view the content of those files.
- final PathMapper pathMapper;
+ final PathMapper? pathMapper;
/// The auth token for the current site, for use in generating URIs.
final String authToken;
@@ -70,7 +70,7 @@
this.unitInfo, this.migrationInfo, this.pathMapper, this.authToken);
/// Return the path context used to manipulate paths.
- path.Context get pathContext => migrationInfo.pathContext;
+ path.Context get pathContext => migrationInfo!.pathContext;
/// Builds a JSON view of the instrumentation information in [unitInfo].
FileDetails render() {
@@ -148,7 +148,7 @@
if (target.filePath != unitInfo.path ||
region.offset != target.offset) {
var openInsertion = openInsertions[openOffset] ?? '';
- var targetUri = _uriForPath(pathMapper.map(target.filePath), target);
+ var targetUri = _uriForPath(pathMapper!.map(target.filePath), target);
openInsertion =
'<a href="$targetUri" class="nav-link">$openInsertion';
openInsertions[openOffset] = openInsertion;
@@ -169,12 +169,12 @@
var previousOffset = 0;
for (var offset in offsets) {
navContent2.write(
- _htmlEscape.convert(content.substring(previousOffset, offset)));
+ _htmlEscape.convert(content!.substring(previousOffset, offset)));
navContent2.write(closeInsertions[offset] ?? '');
navContent2.write(openInsertions[offset] ?? '');
previousOffset = offset;
}
- if (previousOffset < content.length) {
+ if (previousOffset < content!.length) {
navContent2.write(_htmlEscape.convert(content.substring(previousOffset)));
}
return navContent2.toString();
@@ -186,14 +186,14 @@
/// The content of the file (not including added links and anchors) will be
/// HTML-escaped.
String _computeRegionContent(UnitInfo unit) {
- var content = unitInfo.content;
+ var content = unitInfo.content!;
var rows = <String>[];
var currentTextCell = StringBuffer();
bool isAddedLine = false;
var lineNumber = 1;
void finishRow(bool isAddedText) {
- var line = currentTextCell?.toString();
+ var line = currentTextCell.toString();
if (isAddedLine) {
rows.add('<tr><td class="line-no">(new)</td><td>$line</td></tr>');
} else {
@@ -242,7 +242,6 @@
case RegionType.informative:
return 'informative-region';
}
- throw StateError('Unexpected RegionType $type');
}
var previousOffset = 0;
@@ -270,7 +269,7 @@
writeSplitLines(content.substring(previousOffset));
}
finishRow(false);
- return '<table data-path="${pathMapper.map(unit.path)}"><tbody>'
+ return '<table data-path="${pathMapper!.map(unit.path!)}"><tbody>'
'${rows.join()}</tbody></table>';
}
@@ -312,10 +311,8 @@
'source)';
case NullabilityFixKind.conditionTrueInStrongMode:
return '$count condition$s will be true in strong checking mode';
- break;
case NullabilityFixKind.conditionFalseInStrongMode:
return '$count condition$s will be false in strong checking mode';
- break;
case NullabilityFixKind.makeTypeNullable:
return '$count type$s made nullable';
case NullabilityFixKind.makeTypeNullableDueToHint:
@@ -341,7 +338,6 @@
case NullabilityFixKind.typeNotMadeNullableDueToHint:
return '$count type$s not made nullable due to hint$s';
}
- throw StateError('Null kind');
}
/// Returns the URL that will navigate to the given [target] in the file at
diff --git a/pkg/nnbd_migration/lib/src/front_end/web/edit_details.dart b/pkg/nnbd_migration/lib/src/front_end/web/edit_details.dart
index f5ad437..53a17bf 100644
--- a/pkg/nnbd_migration/lib/src/front_end/web/edit_details.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/web/edit_details.dart
@@ -2,7 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-import 'package:meta/meta.dart';
import 'package:nnbd_migration/src/hint_action.dart';
/// Information about what should be populated into the "Edit Details" view of
@@ -11,54 +10,54 @@
/// A list of edits that can be offered to the user related to this source
/// location (e.g. adding/removing hints). `null` if this feature is
/// disabled.
- final List<EditLink> edits;
+ final List<EditLink>? edits;
/// A string explanation of the edit.
- final String explanation;
+ final String? explanation;
/// The line number of the edit.
- final int line;
+ final int? line;
/// The path of the file that was edited, to be shown to the user.
- final String displayPath;
+ final String? displayPath;
/// The path of the file that was edited, as a URI.
- final String uriPath;
+ final String? uriPath;
/// A list of traces representing stacktrace-like views of why the change was
/// made, or the empty list if there are no traces for this change.
- final List<Trace> traces;
+ final List<Trace>? traces;
EditDetails(
{this.edits,
- @required this.explanation,
- @required this.line,
- @required this.displayPath,
- @required this.uriPath,
+ required this.explanation,
+ required this.line,
+ required this.displayPath,
+ required this.uriPath,
this.traces = const []});
EditDetails.fromJson(dynamic json)
: edits = _decodeEdits(json['edits']),
- explanation = json['explanation'] as String,
- line = json['line'] as int,
- displayPath = json['displayPath'] as String,
- uriPath = json['uriPath'] as String,
+ explanation = json['explanation'] as String?,
+ line = json['line'] as int?,
+ displayPath = json['displayPath'] as String?,
+ uriPath = json['uriPath'] as String?,
traces = _decodeTraces(json['traces']);
- Map<String, Object> toJson() => {
- if (edits != null) 'edits': [for (var edit in edits) edit.toJson()],
+ Map<String, Object?> toJson() => {
+ if (edits != null) 'edits': [for (var edit in edits!) edit.toJson()],
'explanation': explanation,
'line': line,
'displayPath': displayPath,
'uriPath': uriPath,
if (traces != null)
- 'traces': [for (var trace in traces) trace.toJson()],
+ 'traces': [for (var trace in traces!) trace.toJson()],
};
- static List<EditLink> _decodeEdits(dynamic json) =>
+ static List<EditLink>? _decodeEdits(dynamic json) =>
json == null ? null : [for (var edit in json) EditLink.fromJson(edit)];
- static List<Trace> _decodeTraces(dynamic json) =>
+ static List<Trace>? _decodeTraces(dynamic json) =>
json == null ? null : [for (var trace in json) Trace.fromJson(trace)];
}
@@ -68,18 +67,18 @@
/// or remove a hint).
class EditLink {
/// Description of the change to be performed.
- final String description;
+ final String? description;
/// The href to link to.
- final String href;
+ final String? href;
- EditLink({@required this.description, @required this.href});
+ EditLink({required this.description, required this.href});
EditLink.fromJson(dynamic json)
- : description = json['description'] as String,
- href = json['href'] as String;
+ : description = json['description'] as String?,
+ href = json['href'] as String?;
- Map<String, Object> toJson() => {
+ Map<String, Object?> toJson() => {
'description': description,
'href': href,
};
@@ -91,22 +90,22 @@
/// information about the rationale for a change.
class TargetLink {
/// The href to link to.
- final String href;
+ final String? href;
/// The line number of the link.
- final int line;
+ final int? line;
/// Relative path to the source file (intended for display).
- final String path;
+ final String? path;
- TargetLink({@required this.href, @required this.line, @required this.path});
+ TargetLink({required this.href, required this.line, required this.path});
TargetLink.fromJson(dynamic json)
- : href = json['href'] as String,
- line = json['line'] as int,
- path = json['path'] as String;
+ : href = json['href'] as String?,
+ line = json['line'] as int?,
+ path = json['path'] as String?;
- Map<String, Object> toJson() => {
+ Map<String, Object?> toJson() => {
'href': href,
'line': line,
'path': path,
@@ -116,20 +115,20 @@
/// A trace of why a nullability decision was made.
class Trace {
/// Text description of the trace.
- final String description;
+ final String? description;
/// List of trace entries.
final List<TraceEntry> entries;
- Trace({@required this.description, @required this.entries});
+ Trace({required this.description, required this.entries});
Trace.fromJson(dynamic json)
- : description = json['description'] as String,
+ : description = json['description'] as String?,
entries = [
for (var entry in json['entries']) TraceEntry.fromJson(entry)
];
- Map<String, Object> toJson() => {
+ Map<String, Object?> toJson() => {
'description': description,
'entries': [for (var entry in entries) entry.toJson()]
};
@@ -138,46 +137,46 @@
/// Information about a single entry in a nullability trace.
class TraceEntry {
/// Text description of the entry.
- final String description;
+ final String? description;
/// The function associated with the entry. We display this before the link
/// so that the trace has the familiar appearance of a stacktrace.
///
/// Null if not known.
- final String function;
+ final String? function;
/// Source code location associated with the entry, or `null` if no source
/// code location is known.
- final TargetLink link;
+ final TargetLink? link;
/// The hint actions available to affect this entry of the trace, or `[]` if
/// none.
final List<HintAction> hintActions;
TraceEntry(
- {@required this.description,
+ {required this.description,
this.function,
this.link,
this.hintActions = const []});
TraceEntry.fromJson(dynamic json)
- : description = json['description'] as String,
- function = json['function'] as String,
+ : description = json['description'] as String?,
+ function = json['function'] as String?,
link = _decodeLink(json['link']),
- hintActions = (json['hintActions'] as List)
+ hintActions = (json['hintActions'] as List?)
?.map((value) =>
- HintAction.fromJson(value as Map<String, Object>))
- ?.toList() ??
+ HintAction.fromJson(value as Map<String, Object?>))
+ .toList() ??
const [];
- Map<String, Object> toJson() => {
+ Map<String, Object?> toJson() => {
'description': description,
if (function != null) 'function': function,
- if (link != null) 'link': link.toJson(),
+ if (link != null) 'link': link!.toJson(),
if (hintActions.isNotEmpty)
'hintActions': hintActions.map((action) => action.toJson()).toList()
};
- static TargetLink _decodeLink(dynamic json) =>
+ static TargetLink? _decodeLink(dynamic json) =>
json == null ? null : TargetLink.fromJson(json);
}
diff --git a/pkg/nnbd_migration/lib/src/front_end/web/file_details.dart b/pkg/nnbd_migration/lib/src/front_end/web/file_details.dart
index 1bf9e26..d9174a8 100644
--- a/pkg/nnbd_migration/lib/src/front_end/web/file_details.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/web/file_details.dart
@@ -2,28 +2,26 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-import 'package:meta/meta.dart';
-
/// Information about an item that should show up in the "proposed edits" panel.
class EditListItem {
/// Line number of this edit.
- final int line;
+ final int? line;
/// Human-readable explanation of this edit.
- final String explanation;
+ final String? explanation;
/// File offset of this edit
- final int offset;
+ final int? offset;
EditListItem(
- {@required this.line, @required this.explanation, @required this.offset});
+ {required this.line, required this.explanation, required this.offset});
EditListItem.fromJson(dynamic json)
- : line = json['line'] as int,
- explanation = json['explanation'] as String,
- offset = json['offset'] as int;
+ : line = json['line'] as int?,
+ explanation = json['explanation'] as String?,
+ offset = json['offset'] as int?;
- Map<String, Object> toJson() =>
+ Map<String, Object?> toJson() =>
{'line': line, 'explanation': explanation, 'offset': offset};
}
@@ -33,7 +31,7 @@
/// added, removed, and unchanged file regions.
///
/// TODO(paulberry): this should be replaced by a more neutral data structure.
- final String regions;
+ final String? regions;
/// HTML representation of the source file with links added to allow
/// navigation through source files.
@@ -42,11 +40,11 @@
/// to a specific line number.
///
/// TODO(paulberry): this should be replaced by a more neutral data structure.
- final String navigationContent;
+ final String? navigationContent;
/// Textual representation of the source file, including both added and
/// removed text.
- final String sourceCode;
+ final String? sourceCode;
/// Items that should show up in the "proposed edits" panel for the file.
///
@@ -56,10 +54,10 @@
final Map<String, List<EditListItem>> edits;
FileDetails(
- {@required this.regions,
- @required this.navigationContent,
- @required this.sourceCode,
- @required this.edits});
+ {required this.regions,
+ required this.navigationContent,
+ required this.sourceCode,
+ required this.edits});
FileDetails.empty()
: regions = '',
@@ -68,17 +66,18 @@
edits = const {};
FileDetails.fromJson(dynamic json)
- : regions = json['regions'] as String,
- navigationContent = json['navigationContent'] as String,
- sourceCode = json['sourceCode'] as String,
+ : regions = json['regions'] as String?,
+ navigationContent = json['navigationContent'] as String?,
+ sourceCode = json['sourceCode'] as String?,
edits = {
- for (var entry in (json['edits'] as Map<String, Object>).entries)
+ for (var entry in (json['edits'] as Map<String, Object?>).entries)
entry.key: [
- for (var edit in entry.value) EditListItem.fromJson(edit)
+ for (var edit in entry.value as Iterable<dynamic>)
+ EditListItem.fromJson(edit)
]
};
- Map<String, Object> toJson() => {
+ Map<String, Object?> toJson() => {
'regions': regions,
'navigationContent': navigationContent,
'sourceCode': sourceCode,
diff --git a/pkg/nnbd_migration/lib/src/front_end/web/highlight_js.dart b/pkg/nnbd_migration/lib/src/front_end/web/highlight_js.dart
index 82e7daf..4aeeb05 100644
--- a/pkg/nnbd_migration/lib/src/front_end/web/highlight_js.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/web/highlight_js.dart
@@ -9,11 +9,11 @@
/// A small wrapper around the JavaScript highlight.js APIs.
class HighlightJs {
- static JsObject get _hljs => context['hljs'] as JsObject;
+ static JsObject? get _hljs => context['hljs'] as JsObject?;
HighlightJs._();
void highlightBlock(Element block) {
- _hljs.callMethod('highlightBlock', [block]);
+ _hljs!.callMethod('highlightBlock', [block]);
}
}
diff --git a/pkg/nnbd_migration/lib/src/front_end/web/migration.dart b/pkg/nnbd_migration/lib/src/front_end/web/migration.dart
index 2e40c87..05b4c7d 100644
--- a/pkg/nnbd_migration/lib/src/front_end/web/migration.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/web/migration.dart
@@ -4,7 +4,6 @@
// Explicitly opt out this file from null safety, for build reasons, inside
// Google.
-// @dart=2.9
import 'dart:async';
import 'dart:convert';
@@ -30,37 +29,37 @@
loadNavigationTree();
if (path != '/' && path != rootPath) {
// TODO(srawlins): replaceState?
- loadFile(path, offset, lineNumber, true, callback: () {
+ loadFile(path!, offset, lineNumber, true, callback: () {
pushState(path, offset, lineNumber);
});
}
- final applyMigrationButton = document.querySelector('.apply-migration');
+ final applyMigrationButton = document.querySelector('.apply-migration')!;
applyMigrationButton.onClick.listen((event) {
if (window.confirm(
"This will apply the changes you've previewed to your working "
'directory. It is recommended you commit any changes you made before '
'doing this.')) {
var navigationTreeJson = [
- for (var entity in navigationTree) entity.toJson()
+ for (var entity in navigationTree!) entity.toJson()
];
doPost('/apply-migration', {'navigationTree': navigationTreeJson})
.then((xhr) {
- document.body.classes
+ document.body!.classes
..remove('proposed')
..add('applied');
- }).catchError((e, st) {
+ }).catchError((Object e, st) {
handleError("Couldn't apply migration", e, st);
});
}
});
- final rerunMigrationButton = document.querySelector('.rerun-migration');
+ final rerunMigrationButton = document.querySelector('.rerun-migration')!;
rerunMigrationButton.onClick.listen((event) async {
try {
- document.body.classes..add('rerunning');
+ document.body!.classes..add('rerunning');
var response = await doPost('/rerun-migration');
- if (response['success'] as bool) {
+ if (response!['success'] as bool) {
window.location.reload();
} else {
handleRerunFailure(response['errors'] as List<Object>);
@@ -68,27 +67,27 @@
} catch (e, st) {
handleError('Failed to rerun migration', e, st);
} finally {
- document.body.classes.remove('rerunning');
+ document.body!.classes.remove('rerunning');
}
});
- final reportProblemButton = document.querySelector('.report-problem');
+ final reportProblemButton = document.querySelector('.report-problem')!;
reportProblemButton.onClick.listen((_) {
window.open(getGitHubProblemUri().toString(), 'report-problem');
});
- document.querySelector('.popup-pane .close').onClick.listen(
- (_) => document.querySelector('.popup-pane').style.display = 'none');
+ document.querySelector('.popup-pane .close')!.onClick.listen(
+ (_) => document.querySelector('.popup-pane')!.style.display = 'none');
- migrateUnitStatusIcon.onClick.listen((MouseEvent event) {
- var unitPath = unitName.innerText;
+ migrateUnitStatusIcon!.onClick.listen((MouseEvent event) {
+ var unitPath = unitName!.innerText;
var unitNavItem = document
- .querySelector('.nav-panel [data-name*="${Css.escape(unitPath)}"]')
+ .querySelector('.nav-panel [data-name*="${Css.escape(unitPath)}"]')!
.parentNode as Element;
var statusIcon = unitNavItem.querySelector('.status-icon');
- var entity = navigationTree.find(unitPath);
+ var entity = navigationTree!.find(unitPath);
if (entity is NavigationTreeFileNode &&
- entity.migrationStatusCanBeChanged) {
+ entity.migrationStatusCanBeChanged!) {
toggleFileMigrationStatus(entity);
updateIconsForNode(statusIcon, entity);
updateParentIcons(unitNavItem, entity);
@@ -97,7 +96,7 @@
});
window.addEventListener('popstate', (event) {
- var path = window.location.pathname;
+ var path = window.location.pathname!;
var offset = getOffset(window.location.href);
var lineNumber = getLine(window.location.href);
if (path.length > 1) {
@@ -117,34 +116,34 @@
// server would attach such a token to cookies. We could do a little step where
// the first request to the server with the token is considered
// "authentication", and we subsequently store the token in cookies thereafter.
-final String authToken =
+final String? authToken =
Uri.parse(window.location.href).queryParameters['authToken'];
-final Element editListElement =
+final Element? editListElement =
document.querySelector('.edit-list .panel-content');
-final Element editPanel = document.querySelector('.edit-panel .panel-content');
+final Element? editPanel = document.querySelector('.edit-panel .panel-content');
-final Element footerPanel = document.querySelector('footer');
+final Element? footerPanel = document.querySelector('footer');
-final Element headerPanel = document.querySelector('header');
+final Element? headerPanel = document.querySelector('header');
-final Element migrateUnitStatusIcon =
+final Element? migrateUnitStatusIcon =
document.querySelector('#migrate-unit-status-icon');
-final Element migrateUnitStatusIconLabel =
+final Element? migrateUnitStatusIconLabel =
document.querySelector('#migrate-unit-status-icon-label');
-List<NavigationTreeNode> /*?*/ navigationTree;
+List<NavigationTreeNode>? navigationTree;
-final Element unitName = document.querySelector('#unit-name');
+final Element? unitName = document.querySelector('#unit-name');
-String get rootPath => querySelector('.root').text.trim();
+String get rootPath => querySelector('.root')!.text!.trim();
-String get sdkVersion => document.getElementById('sdk-version').text;
+String? get sdkVersion => document.getElementById('sdk-version')!.text;
void addArrowClickHandler(Element arrow) {
- var childList = (arrow.parentNode as Element).querySelector(':scope > ul');
+ var childList = (arrow.parentNode as Element).querySelector(':scope > ul')!;
// Animating height from "auto" to "0" is not supported by CSS [1], so all we
// have are hacks. The `* 2` allows for events in which the list grows in
// height when resized, with additional text wrapping.
@@ -162,7 +161,7 @@
}
void addClickHandlers(String selector, bool clearEditDetails) {
- var parentElement = document.querySelector(selector);
+ var parentElement = document.querySelector(selector)!;
// Add navigation handlers for navigation links in the source code.
List<Element> navLinks = parentElement.querySelectorAll('.nav-link');
@@ -172,13 +171,13 @@
List<Element> regions = parentElement.querySelectorAll('.region');
if (regions.isNotEmpty) {
- var table = parentElement.querySelector('table[data-path]');
+ var table = parentElement.querySelector('table[data-path]')!;
var path = table.dataset['path'];
regions.forEach((Element anchor) {
anchor.onClick.listen((event) {
- var offset = int.parse(anchor.dataset['offset']);
- var line = int.parse(anchor.dataset['line']);
- loadAndPopulateEditDetails(path, offset, line);
+ var offset = int.parse(anchor.dataset['offset']!);
+ var line = int.parse(anchor.dataset['line']!);
+ loadAndPopulateEditDetails(path!, offset, line);
});
});
}
@@ -200,14 +199,14 @@
///
/// Returns a T so that the various json objects can be requested (lists, maps,
/// etc.).
-Future<T> doGet<T>(String path,
+Future<T?> doGet<T>(String path,
{Map<String, String> queryParameters = const {}}) =>
doRequest(HttpRequest()
..open('GET', pathWithQueryParameters(path, queryParameters), async: true)
..setRequestHeader('Content-Type', 'application/json; charset=UTF-8'));
/// Perform a POST request on the path, return the JSON-decoded response.
-Future<Map<String, Object>> doPost(String path, [Object body]) => doRequest(
+Future<Map<String, Object?>?> doPost(String path, [Object? body]) => doRequest(
HttpRequest()
..open('POST', pathWithQueryParameters(path, {}), async: true)
..setRequestHeader('Content-Type', 'application/json; charset=UTF-8'),
@@ -220,7 +219,7 @@
/// the response body on a non-200 code. We want to get that response body in
/// that case, though, because it may be an error response from the server with
/// useful debugging information (stack trace etc).
-Future<T> doRequest<T>(HttpRequest xhr, [Object body]) async {
+Future<T?> doRequest<T>(HttpRequest xhr, [Object? body]) async {
var completer = Completer<HttpRequest>();
xhr.onLoad.listen((e) {
completer.complete(xhr);
@@ -272,12 +271,12 @@
check its output for a fresh URL, and use that URL to perform your migration.
''');
}
- final json = jsonDecode(xhr.responseText);
+ final Object? json = jsonDecode(xhr.responseText!);
if (xhr.status == 200) {
// Request OK.
- return json as T;
+ return json as T?;
} else {
- throw json;
+ throw json!;
}
}
@@ -285,7 +284,7 @@
/// pre-populating the title, some labels, using [description], [exception], and
/// [stackTrace] in the body.
Uri getGitHubErrorUri(
- String description, Object exception, Object stackTrace) =>
+ String description, Object? exception, Object? stackTrace) =>
Uri.https('github.com', 'dart-lang/sdk/issues/new', {
'title': 'Customer-reported issue with NNBD migration tool: $description',
'labels': 'area-analyzer,analyzer-nnbd-migration,type-bug',
@@ -334,18 +333,18 @@
''',
});
-int getLine(String location) {
+int? getLine(String location) {
var str = Uri.parse(location).queryParameters['line'];
return str == null ? null : int.tryParse(str);
}
-int getOffset(String location) {
+int? getOffset(String location) {
var str = Uri.parse(location).queryParameters['offset'];
return str == null ? null : int.tryParse(str);
}
void handleAddHintLinkClick(MouseEvent event) async {
- var path = (event.currentTarget as Element).getAttribute('href');
+ var path = (event.currentTarget as Element).getAttribute('href')!;
// Don't navigate on link click.
event.preventDefault();
@@ -355,22 +354,22 @@
// Directing the server to produce an edit; request it, then do work with
// the response.
await doPost(path);
- await loadFile(window.location.pathname, null, null, false);
- document.body.classes.add('needs-rerun');
+ await loadFile(window.location.pathname!, null, null, false);
+ document.body!.classes.add('needs-rerun');
_scrollContentTo(previousScrollPosition);
} catch (e, st) {
handleError("couldn't add/remove hint", e, st);
}
}
-void handleError(String header, Object exception, Object stackTrace) {
- String subheader;
- Object details;
+void handleError(String header, Object exception, Object? stackTrace) {
+ String? subheader;
+ Object? details;
if (exception is Map<String, Object> &&
exception['success'] == false &&
exception.containsKey('exception') &&
exception.containsKey('stackTrace')) {
- subheader = exception['exception'] as String;
+ subheader = exception['exception'] as String?;
stackTrace = exception['stackTrace'];
} else if (exception is UserError) {
subheader = exception.message;
@@ -382,10 +381,10 @@
}
// If there was no detailed error message, use the stacktrace instead.
details ??= stackTrace;
- final popupPane = document.querySelector('.popup-pane');
- popupPane.querySelector('h2').innerText = header;
- popupPane.querySelector('p').innerText = subheader;
- popupPane.querySelector('pre').innerText = details.toString();
+ final popupPane = document.querySelector('.popup-pane')!;
+ popupPane.querySelector('h2')!.innerText = header;
+ popupPane.querySelector('p')!.innerText = subheader!;
+ popupPane.querySelector('pre')!.innerText = details.toString();
var bottom = popupPane.querySelector('a.bottom') as AnchorElement;
bottom
..href = getGitHubErrorUri(header, subheader, stackTrace).toString()
@@ -398,7 +397,7 @@
Element target = event.currentTarget as Element;
event.preventDefault();
- var location = target.getAttribute('href');
+ var location = target.getAttribute('href')!;
var path = _stripQuery(location);
var offset = getOffset(location);
@@ -416,15 +415,15 @@
}
void handleRerunFailure(List<Object> errors) {
- final popupPane = document.querySelector('.popup-pane');
- popupPane.querySelector('h2').innerText = 'Failed to rerun from sources';
- popupPane.querySelector('p').innerText =
+ final popupPane = document.querySelector('.popup-pane')!;
+ popupPane.querySelector('h2')!.innerText = 'Failed to rerun from sources';
+ popupPane.querySelector('p')!.innerText =
'Sources contain static analysis errors:';
- popupPane.querySelector('pre').innerText = errors.cast<Map>().map((error) {
+ popupPane.querySelector('pre')!.innerText = errors.cast<Map>().map((error) {
return '${error['severity']} - ${error['message']} '
'at ${error['location']} - (${error['code']})';
}).join('\n');
- popupPane.querySelector('a.bottom').style.display = 'none';
+ popupPane.querySelector('a.bottom')!.style.display = 'none';
popupPane.style.display = 'initial';
// TODO(srawlins): I think we should lock down the entire web UI, except for
@@ -438,9 +437,9 @@
}
/// Loads the explanation for [region], into the ".panel-content" div.
-void loadAndPopulateEditDetails(String path, int offset, int line) async {
+void loadAndPopulateEditDetails(String path, int? offset, int? line) async {
try {
- final responseJson = await doGet<Map<String, Object>>(path,
+ final responseJson = await doGet<Map<String, Object?>>(path,
queryParameters: {'region': 'region', 'offset': '$offset'});
var response = EditDetails.fromJson(responseJson);
populateEditDetails(response);
@@ -455,10 +454,10 @@
/// view.
Future<void> loadFile(
String path,
- int offset,
- int line,
+ int? offset,
+ int? line,
bool clearEditDetails, {
- VoidCallback callback,
+ VoidCallback? callback,
}) async {
// Handle the case where we're requesting a directory.
if (!path.endsWith('.dart')) {
@@ -473,7 +472,7 @@
try {
// Navigating to another file; request it, then do work with the response.
- final response = await doGet<Map<String, Object>>(path,
+ final response = await doGet<Map<String, Object?>>(path,
queryParameters: {'inline': 'true'});
writeCodeAndRegions(path, FileDetails.fromJson(response), clearEditDetails);
maybeScrollToAndHighlight(offset, line);
@@ -493,18 +492,18 @@
// Request the navigation tree, then do work with the response.
try {
- final response = await doGet<List<Object>>(path);
- var navTree = document.querySelector('.nav-tree');
+ final response = await doGet<List<Object?>>(path);
+ var navTree = document.querySelector('.nav-tree')!;
navTree.innerHtml = '';
navigationTree = NavigationTreeNode.listFromJson(response);
- writeNavigationSubtree(navTree, navigationTree,
+ writeNavigationSubtree(navTree, navigationTree!,
enablePartialMigration: true);
} catch (e, st) {
handleError("couldn't load navigation tree", e, st);
}
}
-void logError(Object e, Object st) {
+void logError(Object e, Object? st) {
window.console.error('$e');
window.console.error('$st');
}
@@ -516,9 +515,9 @@
// only choose to _not_ scroll a line of code into view if the entire line is
// visible.
var lineHeight = 14;
- var visibleCeiling = headerPanel.offsetHeight + lineHeight;
+ var visibleCeiling = headerPanel!.offsetHeight + lineHeight;
var visibleFloor =
- window.innerHeight - (footerPanel.offsetHeight + lineHeight);
+ window.innerHeight! - (footerPanel!.offsetHeight + lineHeight);
if (rect.bottom > visibleFloor) {
element.scrollIntoView();
} else if (rect.top < visibleCeiling) {
@@ -534,9 +533,9 @@
/// class to the entire line on which the target lies.
///
/// If [offset] is null, instead scrolls to the top of the file.
-void maybeScrollToAndHighlight(int offset, int lineNumber) {
- Element target;
- Element line;
+void maybeScrollToAndHighlight(int? offset, int? lineNumber) {
+ Element? target;
+ Element? line;
if (offset != null) {
target = document.getElementById('o$offset');
@@ -547,7 +546,7 @@
} else if (line != null) {
// If the target doesn't exist, but the line does, scroll that into view
// instead.
- maybeScrollIntoView(line.parent);
+ maybeScrollIntoView(line.parent!);
}
if (line != null) {
(line.parentNode as Element).classes.add('highlight');
@@ -569,11 +568,11 @@
/// If [callback] is present, it will be called after the server response has
/// been processed, and the content has been updated on the page.
void navigate(
- String path,
- int offset,
- int lineNumber,
+ String? path,
+ int? offset,
+ int? lineNumber,
bool clearEditDetails, {
- VoidCallback callback,
+ VoidCallback? callback,
}) {
var currentOffset = getOffset(window.location.href);
var currentLineNumber = getLine(window.location.href);
@@ -585,7 +584,7 @@
callback();
}
} else {
- loadFile(path, offset, lineNumber, clearEditDetails, callback: callback);
+ loadFile(path!, offset, lineNumber, clearEditDetails, callback: callback);
}
}
@@ -605,23 +604,23 @@
return uri.replace(queryParameters: mergedQueryParameters).toString();
}
-String pluralize(int count, String single, {String multiple}) {
+String pluralize(int count, String single, {String? multiple}) {
return count == 1 ? single : (multiple ?? '${single}s');
}
-void populateEditDetails([EditDetails response]) {
+void populateEditDetails([EditDetails? response]) {
// Clear out any current edit details.
- editPanel.innerHtml = '';
+ editPanel!.innerHtml = '';
if (response == null) {
Element p = ParagraphElement()
..text = 'See details about a proposed edit.'
..classes = ['placeholder'];
- editPanel.append(p);
+ editPanel!.append(p);
p.scrollIntoView();
return;
}
- var fileDisplayPath = response.displayPath;
+ var fileDisplayPath = response.displayPath!;
var parentDirectory = _p.dirname(fileDisplayPath);
// 'Changed ... at foo.dart:12.'
@@ -629,12 +628,12 @@
var relPath = _p.relative(fileDisplayPath, from: rootPath);
var line = response.line;
Element explanation = document.createElement('p');
- editPanel.append(explanation);
+ editPanel!.append(explanation);
explanation
..appendText('$explanationMessage at ')
..append(AnchorElement(
href: pathWithQueryParameters(
- response.uriPath, {'line': line.toString()}))
+ response.uriPath!, {'line': line.toString()}))
..appendText('$relPath:$line.'));
explanation.scrollIntoView();
_populateEditTraces(response, editPanel, parentDirectory);
@@ -644,21 +643,21 @@
/// Write the contents of the Edit List, from JSON data [editListData].
void populateProposedEdits(
String path, Map<String, List<EditListItem>> edits, bool clearEditDetails) {
- editListElement.innerHtml = '';
+ editListElement!.innerHtml = '';
var editCount = edits.length;
if (editCount == 0) {
Element p = document.createElement('p');
- editListElement.append(p);
+ editListElement!.append(p);
p.append(Text('No proposed edits'));
} else {
for (var entry in edits.entries) {
Element p = document.createElement('p');
- editListElement.append(p);
+ editListElement!.append(p);
p.append(Text('${entry.key}:'));
Element list = document.createElement('ul');
- editListElement.append(list);
+ editListElement!.append(list);
for (var edit in entry.value) {
Element item = document.createElement('li');
list.append(item);
@@ -673,7 +672,7 @@
anchor.append(Text('line $line'));
anchor.setAttribute(
'href',
- pathWithQueryParameters(window.location.pathname, {
+ pathWithQueryParameters(window.location.pathname!, {
'line': '$line',
'offset': '$offset',
}));
@@ -694,7 +693,7 @@
}
}
-void pushState(String path, int offset, int line) {
+void pushState(String? path, int? offset, int? line) {
var uri = Uri.parse('${window.location.origin}$path');
var params = {
@@ -710,7 +709,7 @@
/// If [path] lies within [root], return the relative path of [path] from [root].
/// Otherwise, return [path].
String relativePath(String path) {
- var root = querySelector('.root').text + '/';
+ var root = querySelector('.root')!.text! + '/';
if (path.startsWith(root)) {
return path.substring(root.length);
} else {
@@ -719,7 +718,7 @@
}
/// Remove highlighting from [offset].
-void removeHighlight(int offset, int lineNumber) {
+void removeHighlight(int? offset, int? lineNumber) {
if (offset != null) {
var anchor = document.getElementById('o$offset');
if (anchor != null) {
@@ -729,7 +728,7 @@
if (lineNumber != null) {
var line = document.querySelector('.line-$lineNumber');
if (line != null) {
- line.parent.classes.remove('highlight');
+ line.parent!.classes.remove('highlight');
}
}
}
@@ -771,32 +770,35 @@
case UnitMigrationStatus.indeterminate:
throw StateError('File ${entity.path} should not have '
'indeterminate migration status');
+ default:
+ // TODO(paulberry): this should never happen.
+ break;
}
}
/// Updates [icon] according to [status].
-void updateIconForStatus(Element icon, UnitMigrationStatus status) {
+void updateIconForStatus(Element? icon, UnitMigrationStatus? status) {
switch (status) {
case UnitMigrationStatus.alreadyMigrated:
- icon.innerText = 'check_box';
+ icon!.innerText = 'check_box';
icon.classes.add('already-migrated');
icon.classes.add('disabled');
icon.setAttribute('title', 'Already migrated');
break;
case UnitMigrationStatus.migrating:
- icon.innerText = 'check_box';
+ icon!.innerText = 'check_box';
icon.classes.remove('opted-out');
icon.classes.add('migrating');
icon.setAttribute('title', 'Migrating to null safety');
break;
case UnitMigrationStatus.optingOut:
- icon.innerText = 'check_box_outline_blank';
+ icon!.innerText = 'check_box_outline_blank';
icon.classes.remove('migrating');
icon.classes.add('opted-out');
icon.setAttribute('title', 'Opting out of null safety');
break;
default:
- icon.innerText = 'indeterminate_check_box';
+ icon!.innerText = 'indeterminate_check_box';
icon.classes.remove('migrating');
// 'opted-out' is the same style as 'indeterminate'.
icon.classes.add('opted-out');
@@ -808,18 +810,18 @@
/// Updates the navigation [icon] and current file icon according to the current
/// migration status of [entity].
-void updateIconsForNode(Element icon, NavigationTreeNode entity) {
+void updateIconsForNode(Element? icon, NavigationTreeNode entity) {
var status = entity.migrationStatus;
updateIconForStatus(icon, status);
// Update the status at the top of the file view if [entity] represents the
// current file.
- var unitPath = unitName.innerText;
+ var unitPath = unitName!.innerText;
if (entity.path == unitPath) {
if (entity is NavigationTreeFileNode &&
- !entity.migrationStatusCanBeChanged) {
- icon.classes.add('disabled');
+ !entity.migrationStatusCanBeChanged!) {
+ icon!.classes.add('disabled');
} else {
- icon.classes.remove('disabled');
+ icon!.classes.remove('disabled');
}
updateIconForStatus(migrateUnitStatusIcon, status);
}
@@ -828,10 +830,10 @@
/// Update the heading and navigation links.
///
/// Call this after updating page content on a navigation.
-void updatePage(String path, [int offset]) {
+void updatePage(String path, [int? offset]) {
path = relativePath(path);
// Update page heading.
- unitName.text = path;
+ unitName!.text = path;
// Update navigation styles.
document.querySelectorAll('.nav-panel .nav-link').forEach((Element link) {
var name = link.dataset['name'];
@@ -847,9 +849,9 @@
var entity = navigationTree?.find(path);
// Update migration status for files in current migration.
if (entity == null) {
- migrateUnitStatusIconLabel.classes.remove('visible');
+ migrateUnitStatusIconLabel!.classes.remove('visible');
} else {
- migrateUnitStatusIconLabel.classes.add('visible');
+ migrateUnitStatusIconLabel!.classes.add('visible');
updateIconForStatus(migrateUnitStatusIcon, entity.migrationStatus);
}
}
@@ -867,16 +869,16 @@
}
/// Updates subtree icons for the children [entity] with list item [element].
-void updateSubtreeIcons(Element element, NavigationTreeDirectoryNode entity) {
- for (var child in entity.subtree) {
+void updateSubtreeIcons(Element? element, NavigationTreeDirectoryNode entity) {
+ for (var child in entity.subtree!) {
var childNode =
- element.querySelector('[data-name*="${Css.escape(child.path)}"]');
+ element!.querySelector('[data-name*="${Css.escape(child.path!)}"]');
if (child is NavigationTreeDirectoryNode) {
updateSubtreeIcons(childNode, child);
- var childIcon = childNode.querySelector(':scope > .status-icon');
+ var childIcon = childNode!.querySelector(':scope > .status-icon');
updateIconsForNode(childIcon, entity);
} else {
- var childIcon = (childNode.parentNode as Element)
+ var childIcon = (childNode!.parentNode as Element)
.querySelector(':scope > .status-icon');
updateIconsForNode(childIcon, child);
}
@@ -885,8 +887,8 @@
/// Load data from [data] into the .code and the .regions divs.
void writeCodeAndRegions(String path, FileDetails data, bool clearEditDetails) {
- var regionsElement = document.querySelector('.regions');
- var codeElement = document.querySelector('.code');
+ var regionsElement = document.querySelector('.regions')!;
+ var codeElement = document.querySelector('.code')!;
_PermissiveNodeValidator.setInnerHtml(regionsElement, data.regions);
_PermissiveNodeValidator.setInnerHtml(codeElement, data.navigationContent);
@@ -894,7 +896,7 @@
// highlightAllCode is remarkably slow (about 4 seconds to handle a 300k file
// on a Pixelbook), so skip it for large files.
- if (data.sourceCode.length < 200000) {
+ if (data.sourceCode!.length < 200000) {
highlightAllCode();
}
addClickHandlers('.code', true);
@@ -911,15 +913,15 @@
ul.append(li);
if (entity is NavigationTreeDirectoryNode) {
li.classes.add('dir');
- li.dataset['name'] = entity.path;
+ li.dataset['name'] = entity.path!;
Element arrow = document.createElement('span');
li.append(arrow);
arrow.classes.add('arrow');
arrow.innerHtml = '▼';
var folderIcon = createIcon('folder_open');
li.append(folderIcon);
- li.append(Text(entity.name));
- writeNavigationSubtree(li, entity.subtree,
+ li.append(Text(entity.name!));
+ writeNavigationSubtree(li, entity.subtree!,
enablePartialMigration: enablePartialMigration);
if (enablePartialMigration) {
var statusIcon = createIcon('indeterminate_check_box')
@@ -938,11 +940,11 @@
if (enablePartialMigration) {
var statusIcon = createIcon()..classes.add('status-icon');
if (entity is NavigationTreeFileNode &&
- !entity.migrationStatusCanBeChanged) {
+ !entity.migrationStatusCanBeChanged!) {
statusIcon.classes.add('disabled');
}
updateIconsForNode(statusIcon, entity);
- if (entity.migrationStatusCanBeChanged) {
+ if (entity.migrationStatusCanBeChanged!) {
statusIcon.onClick.listen((MouseEvent event) {
toggleFileMigrationStatus(entity);
updateIconsForNode(statusIcon, entity);
@@ -955,11 +957,11 @@
Element a = document.createElement('a');
li.append(a);
a.classes.add('nav-link');
- a.dataset['name'] = entity.path;
- a.setAttribute('href', pathWithQueryParameters(entity.href, {}));
- a.append(Text(entity.name));
+ a.dataset['name'] = entity.path!;
+ a.setAttribute('href', pathWithQueryParameters(entity.href!, {}));
+ a.append(Text(entity.name!));
a.onClick.listen((MouseEvent event) => handleNavLinkClick(event, true));
- var editCount = entity.editCount;
+ var editCount = entity.editCount!;
if (editCount > 0) {
Element editsBadge = document.createElement('span');
li.append(editsBadge);
@@ -972,41 +974,42 @@
}
}
-void _addHintAction(HintAction hintAction, Node drawer, TargetLink link) {
+void _addHintAction(HintAction hintAction, Node drawer, TargetLink? link) {
drawer.append(ButtonElement()
..onClick.listen((event) async {
try {
var previousScrollPosition = _getCurrentScrollPosition();
await doPost(
pathWithQueryParameters('/apply-hint', {}), hintAction.toJson());
- var path = _stripQuery(link.href);
+ var path = _stripQuery(link!.href!);
await loadFile(path, null, link.line, false);
- document.body.classes.add('needs-rerun');
+ document.body!.classes.add('needs-rerun');
_scrollContentTo(previousScrollPosition);
} catch (e, st) {
handleError("couldn't apply hint", e, st);
}
})
- ..appendText(hintAction.kind.description));
+ ..appendText(hintAction.kind.description!));
}
AnchorElement _aElementForLink(TargetLink link) {
var targetLine = link.line;
AnchorElement a = AnchorElement();
a.append(Text('${link.path}:$targetLine'));
- a.setAttribute('href', link.href);
+ a.setAttribute('href', link.href!);
a.classes.add('nav-link');
return a;
}
-int _getCurrentScrollPosition() => document.querySelector('.content').scrollTop;
+int _getCurrentScrollPosition() =>
+ document.querySelector('.content')!.scrollTop;
-void _populateEditLinks(EditDetails response, Element editPanel) {
+void _populateEditLinks(EditDetails response, Element? editPanel) {
if (response.edits == null) {
return;
}
- var subheading = editPanel.append(document.createElement('p'));
+ var subheading = editPanel!.append(document.createElement('p'));
subheading.append(document.createElement('span')
..classes = ['type-description']
..append(Text('Actions')));
@@ -1014,23 +1017,23 @@
Element editParagraph = document.createElement('p');
editPanel.append(editParagraph);
- for (var edit in response.edits) {
+ for (var edit in response.edits!) {
Element a = document.createElement('a');
editParagraph.append(a);
- a.append(Text(edit.description));
- a.setAttribute('href', edit.href);
+ a.append(Text(edit.description!));
+ a.setAttribute('href', edit.href!);
a.classes = ['add-hint-link', 'before-apply', 'button'];
}
}
void _populateEditTraces(
- EditDetails response, Element editPanel, String parentDirectory) {
- for (var trace in response.traces) {
+ EditDetails response, Element? editPanel, String parentDirectory) {
+ for (var trace in response.traces!) {
var traceParagraph =
- editPanel.append(document.createElement('p')..classes = ['trace']);
+ editPanel!.append(document.createElement('p')..classes = ['trace']);
traceParagraph.append(document.createElement('span')
..classes = ['type-description']
- ..append(Text(trace.description)));
+ ..append(Text(trace.description!)));
traceParagraph.append(Text(':'));
var ul = traceParagraph
.append(document.createElement('ul')..classes = ['trace']);
@@ -1061,7 +1064,7 @@
}
void _scrollContentTo(int top) =>
- document.querySelector('.content').scrollTop = top;
+ document.querySelector('.content')!.scrollTop = top;
String _stripQuery(String path) =>
path.contains('?') ? path.substring(0, path.indexOf('?')) : path;
@@ -1089,17 +1092,17 @@
return true;
}
- static void setInnerHtml(Element element, String html) {
+ static void setInnerHtml(Element element, String? html) {
element.setInnerHtml(html, validator: instance);
}
}
extension on List<NavigationTreeNode> {
/// Finds the node with path equal to [path], recursively, or `null`.
- NavigationTreeNode find(String path) {
+ NavigationTreeNode? find(String path) {
for (var node in this) {
if (node is NavigationTreeDirectoryNode) {
- var foundInSubtree = node.subtree.find(path);
+ var foundInSubtree = node.subtree!.find(path);
if (foundInSubtree != null) return foundInSubtree;
} else {
assert(node is NavigationTreeFileNode);
diff --git a/pkg/nnbd_migration/lib/src/front_end/web/navigation_tree.dart b/pkg/nnbd_migration/lib/src/front_end/web/navigation_tree.dart
index f322e41..16ff587 100644
--- a/pkg/nnbd_migration/lib/src/front_end/web/navigation_tree.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/web/navigation_tree.dart
@@ -2,16 +2,14 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-import 'package:meta/meta.dart';
-
class NavigationTreeDirectoryNode extends NavigationTreeNode {
/// If this is a directory node, list of nodes nested under this one.
/// Otherwise `null`.
- final List<NavigationTreeNode> subtree;
+ final List<NavigationTreeNode>? subtree;
/// Creates a navigation tree node representing a directory.
NavigationTreeDirectoryNode(
- {@required String name, @required String path, @required this.subtree})
+ {required String? name, required String? path, required this.subtree})
: super._(name: name, path: path);
/// Returns the status by examining [subtree]:
@@ -23,11 +21,11 @@
/// 'opting out', then [UnitMigrationStatus.optingOut] is returned.
/// * Otherwise, [UnitMigrationStatus.indeterminate] is returned.
UnitMigrationStatus get migrationStatus {
- if (subtree.isEmpty) return UnitMigrationStatus.alreadyMigrated;
- var sharedStatus = subtree.first.migrationStatus;
+ if (subtree!.isEmpty) return UnitMigrationStatus.alreadyMigrated;
+ var sharedStatus = subtree!.first.migrationStatus;
var allAreMigratedOrMigrating = true;
var allAreMigratedOrOptingOut = true;
- for (var child in subtree) {
+ for (var child in subtree!) {
var childMigrationStatus = child.migrationStatus;
if (childMigrationStatus != sharedStatus) {
@@ -61,7 +59,7 @@
void setSubtreeParents() {
if (subtree != null) {
- for (var child in subtree) {
+ for (var child in subtree!) {
child.parent = this;
}
}
@@ -72,11 +70,11 @@
/// Only child nodes with 'opting out' or 'keep opted out' status are changed.
void toggleChildrenToMigrate() {
//assert(type == NavigationTreeNodeType.directory);
- for (var child in subtree) {
+ for (var child in subtree!) {
if (child is NavigationTreeDirectoryNode) {
child.toggleChildrenToMigrate();
} else if (child is NavigationTreeFileNode &&
- child.migrationStatusCanBeChanged &&
+ child.migrationStatusCanBeChanged! &&
child.migrationStatus == UnitMigrationStatus.optingOut) {
child.migrationStatus = UnitMigrationStatus.migrating;
}
@@ -87,21 +85,21 @@
///
/// Only child nodes with 'migrating' status are changed.
void toggleChildrenToOptOut() {
- for (var child in subtree) {
+ for (var child in subtree!) {
if (child is NavigationTreeDirectoryNode) {
child.toggleChildrenToOptOut();
} else if (child is NavigationTreeFileNode &&
- child.migrationStatusCanBeChanged &&
+ child.migrationStatusCanBeChanged! &&
child.migrationStatus == UnitMigrationStatus.migrating) {
child.migrationStatus = UnitMigrationStatus.optingOut;
}
}
}
- Map<String, Object> toJson() => {
+ Map<String, Object?> toJson() => {
'type': 'directory',
'name': name,
- 'subtree': NavigationTreeNode.listToJson(subtree),
+ 'subtree': NavigationTreeNode.listToJson(subtree!),
if (path != null) 'path': path,
};
}
@@ -109,32 +107,32 @@
class NavigationTreeFileNode extends NavigationTreeNode {
/// If this is a file node, href that should be used if the file is clicked
/// on, otherwise `null`.
- final String href;
+ final String? href;
/// If this is a file node, number of edits that were made in the file,
/// otherwise `null`.
- final int editCount;
+ final int? editCount;
- final bool wasExplicitlyOptedOut;
+ final bool? wasExplicitlyOptedOut;
- UnitMigrationStatus migrationStatus;
+ UnitMigrationStatus? migrationStatus;
- final bool migrationStatusCanBeChanged;
+ final bool? migrationStatusCanBeChanged;
/// Creates a navigation tree node representing a file.
NavigationTreeFileNode(
- {@required String name,
- @required String path,
- @required this.href,
- @required this.editCount,
- @required this.wasExplicitlyOptedOut,
- @required this.migrationStatus,
- @required this.migrationStatusCanBeChanged})
+ {required String? name,
+ required String? path,
+ required this.href,
+ required this.editCount,
+ required this.wasExplicitlyOptedOut,
+ required this.migrationStatus,
+ required this.migrationStatusCanBeChanged})
: super._(name: name, path: path);
NavigationTreeNodeType get type => NavigationTreeNodeType.file;
- Map<String, Object> toJson() => {
+ Map<String, Object?> toJson() => {
'type': 'file',
'name': name,
if (path != null) 'path': path,
@@ -142,7 +140,7 @@
if (editCount != null) 'editCount': editCount,
if (wasExplicitlyOptedOut != null)
'wasExplicitlyOptedOut': wasExplicitlyOptedOut,
- if (migrationStatus != null) 'migrationStatus': migrationStatus.index,
+ if (migrationStatus != null) 'migrationStatus': migrationStatus!.index,
if (migrationStatusCanBeChanged != null)
'migrationStatusCanBeChanged': migrationStatusCanBeChanged,
};
@@ -151,44 +149,45 @@
/// Information about a node in the migration tool's navigation tree.
abstract class NavigationTreeNode {
/// Name of the node.
- final String name;
+ final String? name;
/// Parent of this node, or `null` if this is a top-level node.
- /*late final*/ NavigationTreeNode parent;
+ late final NavigationTreeNode? parent;
/// Relative path to the file or directory from the package root.
- final String path;
+ final String? path;
factory NavigationTreeNode.fromJson(dynamic json) {
- var type = _decodeType(json['type'] as String);
+ var type = _decodeType(json['type'] as String?);
if (type == NavigationTreeNodeType.directory) {
return NavigationTreeDirectoryNode(
- name: json['name'] as String,
- path: json['path'] as String,
+ name: json['name'] as String?,
+ path: json['path'] as String?,
subtree: listFromJsonOrNull(json['subtree']))
..setSubtreeParents();
} else {
return NavigationTreeFileNode(
- name: json['name'] as String,
- path: json['path'] as String,
- href: json['href'] as String,
- editCount: json['editCount'] as int,
- wasExplicitlyOptedOut: json['wasExplicitlyOptedOut'] as bool,
- migrationStatus: _decodeMigrationStatus(json['migrationStatus'] as int),
+ name: json['name'] as String?,
+ path: json['path'] as String?,
+ href: json['href'] as String?,
+ editCount: json['editCount'] as int?,
+ wasExplicitlyOptedOut: json['wasExplicitlyOptedOut'] as bool?,
+ migrationStatus:
+ _decodeMigrationStatus(json['migrationStatus'] as int?),
migrationStatusCanBeChanged:
- json['migrationStatusCanBeChanged'] as bool,
+ json['migrationStatusCanBeChanged'] as bool?,
);
}
}
- NavigationTreeNode._({@required this.name, @required this.path});
+ NavigationTreeNode._({required this.name, required this.path});
/// The migration status of the file or directory.
- UnitMigrationStatus get migrationStatus;
+ UnitMigrationStatus? get migrationStatus;
NavigationTreeNodeType get type;
- Map<String, Object> toJson();
+ Map<String, Object?> toJson();
/// Deserializes a list of navigation tree nodes from a JSON list.
static List<NavigationTreeNode> listFromJson(dynamic json) =>
@@ -196,19 +195,20 @@
/// Deserializes a list of navigation tree nodes from a possibly null JSON
/// list. If the argument is `null`, `null` is returned.
- static List<NavigationTreeNode> listFromJsonOrNull(dynamic json) =>
+ static List<NavigationTreeNode>? listFromJsonOrNull(dynamic json) =>
json == null ? null : listFromJson(json);
/// Serializes a list of navigation tree nodes into JSON.
- static List<Map<String, Object>> listToJson(List<NavigationTreeNode> nodes) =>
+ static List<Map<String, Object?>> listToJson(
+ List<NavigationTreeNode> nodes) =>
[for (var node in nodes) node.toJson()];
- static UnitMigrationStatus _decodeMigrationStatus(int migrationStatus) {
+ static UnitMigrationStatus? _decodeMigrationStatus(int? migrationStatus) {
if (migrationStatus == null) return null;
return UnitMigrationStatus.values[migrationStatus];
}
- static NavigationTreeNodeType _decodeType(String json) {
+ static NavigationTreeNodeType _decodeType(String? json) {
switch (json) {
case 'directory':
return NavigationTreeNodeType.directory;
diff --git a/pkg/nnbd_migration/lib/src/hint_action.dart b/pkg/nnbd_migration/lib/src/hint_action.dart
index 3d2103f..4c868c7 100644
--- a/pkg/nnbd_migration/lib/src/hint_action.dart
+++ b/pkg/nnbd_migration/lib/src/hint_action.dart
@@ -6,15 +6,15 @@
/// action.
class HintAction {
final HintActionKind kind;
- final int nodeId;
+ final int? nodeId;
HintAction(this.kind, this.nodeId);
- HintAction.fromJson(Map<String, Object> json)
- : nodeId = json['nodeId'] as int,
+ HintAction.fromJson(Map<String, Object?> json)
+ : nodeId = json['nodeId'] as int?,
kind = HintActionKind.values
.singleWhere((action) => action.index == json['kind']);
- Map<String, Object> toJson() => {
+ Map<String, Object?> toJson() => {
'nodeId': nodeId,
'kind': kind.index,
};
@@ -49,7 +49,7 @@
/// Extension methods to make [HintActionKind] act as a smart enum.
extension HintActionKindBehaviors on HintActionKind {
/// Get the text description of a [HintActionKind], for display to users.
- String get description {
+ String? get description {
switch (this) {
case HintActionKind.addNullableHint:
return 'Add /*?*/ hint';
@@ -64,8 +64,5 @@
case HintActionKind.changeToNonNullableHint:
return 'Change to /*!*/ hint';
}
-
- assert(false);
- return null;
}
}
diff --git a/pkg/nnbd_migration/lib/src/node_builder.dart b/pkg/nnbd_migration/lib/src/node_builder.dart
index 3c5e862..14fd871 100644
--- a/pkg/nnbd_migration/lib/src/node_builder.dart
+++ b/pkg/nnbd_migration/lib/src/node_builder.dart
@@ -37,31 +37,31 @@
PermissiveModeVisitor<DecoratedType>,
CompletenessTracker<DecoratedType> {
/// Constraint variables and decorated types are stored here.
- final Variables _variables;
+ final Variables? _variables;
@override
- final Source source;
+ final Source? source;
final LineInfo Function(String) _getLineInfo;
/// If the parameters of a function or method are being visited, the
/// [DecoratedType]s of the function's named parameters that have been seen so
/// far. Otherwise `null`.
- Map<String, DecoratedType> _namedParameters;
+ Map<String, DecoratedType?>? _namedParameters;
/// If the parameters of a function or method are being visited, the
/// [DecoratedType]s of the function's positional parameters that have been
/// seen so far. Otherwise `null`.
- List<DecoratedType> _positionalParameters;
+ List<DecoratedType?>? _positionalParameters;
/// If the child types of a node are being visited, the
/// [NullabilityNodeTarget] that should be used in [visitTypeAnnotation].
/// Otherwise `null`.
- NullabilityNodeTarget _target;
+ NullabilityNodeTarget? _target;
- final NullabilityMigrationListener /*?*/ listener;
+ final NullabilityMigrationListener? listener;
- final NullabilityMigrationInstrumentation /*?*/ instrumentation;
+ final NullabilityMigrationInstrumentation? instrumentation;
final NullabilityGraph _graph;
@@ -79,20 +79,20 @@
}
@override
- DecoratedType visitAsExpression(AsExpression node) {
- node.expression?.accept(this);
+ DecoratedType? visitAsExpression(AsExpression node) {
+ node.expression.accept(this);
_pushNullabilityNodeTarget(
- NullabilityNodeTarget.text('cast type'), () => node.type?.accept(this));
+ NullabilityNodeTarget.text('cast type'), () => node.type.accept(this));
return null;
}
@override
- DecoratedType visitCatchClause(CatchClause node) {
+ DecoratedType? visitCatchClause(CatchClause node) {
var exceptionElement = node.exceptionParameter?.staticElement;
var target = exceptionElement == null
? NullabilityNodeTarget.text('exception type')
: NullabilityNodeTarget.element(exceptionElement, _getLineInfo);
- DecoratedType exceptionType = _pushNullabilityNodeTarget(
+ DecoratedType? exceptionType = _pushNullabilityNodeTarget(
target, () => node.exceptionType?.accept(this));
if (node.exceptionParameter != null) {
// If there is no `on Type` part of the catch clause, the type is dynamic.
@@ -102,8 +102,8 @@
instrumentation?.implicitType(
source, node.exceptionParameter, exceptionType);
}
- _variables.recordDecoratedElementType(
- node.exceptionParameter.staticElement, exceptionType);
+ _variables!.recordDecoratedElementType(
+ node.exceptionParameter!.staticElement, exceptionType);
}
if (node.stackTraceParameter != null) {
// The type of stack traces is always StackTrace (non-nullable).
@@ -113,24 +113,24 @@
StackTraceTypeOrigin(source, node.stackTraceParameter));
var stackTraceType =
DecoratedType(_typeProvider.stackTraceType, nullabilityNode);
- _variables.recordDecoratedElementType(
- node.stackTraceParameter.staticElement, stackTraceType);
+ _variables!.recordDecoratedElementType(
+ node.stackTraceParameter!.staticElement, stackTraceType);
instrumentation?.implicitType(
source, node.stackTraceParameter, stackTraceType);
}
node.stackTraceParameter?.accept(this);
- node.body?.accept(this);
+ node.body.accept(this);
return null;
}
@override
- DecoratedType visitClassDeclaration(ClassDeclaration node) {
+ DecoratedType? visitClassDeclaration(ClassDeclaration node) {
node.metadata.accept(this);
node.name.accept(this);
node.typeParameters?.accept(this);
node.nativeClause?.accept(this);
node.members.accept(this);
- var classElement = node.declaredElement;
+ var classElement = node.declaredElement!;
_handleSupertypeClauses(node, classElement, node.extendsClause?.superclass,
node.withClause, node.implementsClause, null);
var constructors = classElement.constructors;
@@ -144,18 +144,19 @@
returnType: decoratedReturnType,
positionalParameters: const [],
namedParameters: {});
- _variables.recordDecoratedElementType(constructorElement, functionType);
+ _variables!
+ .recordDecoratedElementType(constructorElement, functionType);
}
}
return null;
}
@override
- DecoratedType visitClassTypeAlias(ClassTypeAlias node) {
+ DecoratedType? visitClassTypeAlias(ClassTypeAlias node) {
node.metadata.accept(this);
node.name.accept(this);
node.typeParameters?.accept(this);
- var classElement = node.declaredElement;
+ var classElement = node.declaredElement!;
_handleSupertypeClauses(node, classElement, node.superclass,
node.withClause, node.implementsClause, null);
for (var constructorElement in classElement.constructors) {
@@ -167,28 +168,28 @@
var functionType = DecoratedType.forImplicitFunction(
_typeProvider, constructorElement.type, _graph.never, _graph, target,
returnType: decoratedReturnType);
- _variables.recordDecoratedElementType(constructorElement, functionType);
+ _variables!.recordDecoratedElementType(constructorElement, functionType);
for (var parameter in constructorElement.parameters) {
var parameterType = DecoratedType.forImplicitType(
_typeProvider, parameter.type, _graph, target);
- _variables.recordDecoratedElementType(parameter, parameterType);
+ _variables!.recordDecoratedElementType(parameter, parameterType);
}
}
return null;
}
@override
- DecoratedType visitCompilationUnit(CompilationUnit node) {
- _graph.migrating(node.declaredElement.library.source);
- _graph.migrating(node.declaredElement.source);
+ DecoratedType? visitCompilationUnit(CompilationUnit node) {
+ _graph.migrating(node.declaredElement!.library.source);
+ _graph.migrating(node.declaredElement!.source);
return super.visitCompilationUnit(node);
}
@override
- DecoratedType visitConstructorDeclaration(ConstructorDeclaration node) {
+ DecoratedType? visitConstructorDeclaration(ConstructorDeclaration node) {
_handleExecutableDeclaration(
node,
- node.declaredElement,
+ node.declaredElement!,
node.metadata,
null,
null,
@@ -200,62 +201,59 @@
}
@override
- DecoratedType visitConstructorName(ConstructorName node) {
+ DecoratedType? visitConstructorName(ConstructorName node) {
_pushNullabilityNodeTarget(NullabilityNodeTarget.text('constructed type'),
- () => node.type?.accept(this));
+ () => node.type.accept(this));
node.name?.accept(this);
return null;
}
@override
- DecoratedType visitDeclaredIdentifier(DeclaredIdentifier node) {
+ DecoratedType? visitDeclaredIdentifier(DeclaredIdentifier node) {
node.metadata.accept(this);
- var declaredElement = node.declaredElement;
+ var declaredElement = node.declaredElement!;
var target = NullabilityNodeTarget.element(declaredElement, _getLineInfo);
- DecoratedType type =
+ DecoratedType? type =
_pushNullabilityNodeTarget(target, () => node.type?.accept(this));
- if (node.identifier != null) {
- if (type == null) {
- type = DecoratedType.forImplicitType(
- _typeProvider, declaredElement.type, _graph, target);
- instrumentation?.implicitType(source, node, type);
- }
- _variables.recordDecoratedElementType(
- node.identifier.staticElement, type);
+ if (type == null) {
+ type = DecoratedType.forImplicitType(
+ _typeProvider, declaredElement.type, _graph, target);
+ instrumentation?.implicitType(source, node, type);
}
+ _variables!.recordDecoratedElementType(node.identifier.staticElement, type);
return type;
}
@override
- DecoratedType visitDefaultFormalParameter(DefaultFormalParameter node) {
+ DecoratedType? visitDefaultFormalParameter(DefaultFormalParameter node) {
var decoratedType = node.parameter.accept(this);
- var hint = getPrefixHint(node.firstTokenAfterCommentAndMetadata);
+ var hint = getPrefixHint(node.firstTokenAfterCommentAndMetadata!);
if (node.defaultValue != null) {
- node.defaultValue.accept(this);
+ node.defaultValue!.accept(this);
return null;
- } else if (node.declaredElement.hasRequired) {
+ } else if (node.declaredElement!.hasRequired) {
return null;
} else if (hint != null && hint.kind == HintCommentKind.required) {
- _variables.recordRequiredHint(source, node, hint);
+ _variables!.recordRequiredHint(source, node, hint);
return null;
}
if (decoratedType == null) {
throw StateError('No type computed for ${node.parameter.runtimeType} '
- '(${node.parent.parent.toSource()}) offset=${node.offset}');
+ '(${node.parent!.parent!.toSource()}) offset=${node.offset}');
}
- decoratedType.node.trackPossiblyOptional();
+ decoratedType.node!.trackPossiblyOptional();
return null;
}
@override
- DecoratedType visitEnumDeclaration(EnumDeclaration node) {
+ DecoratedType? visitEnumDeclaration(EnumDeclaration node) {
node.metadata.accept(this);
node.name.accept(this);
- var classElement = node.declaredElement;
- _variables.recordDecoratedElementType(
+ var classElement = node.declaredElement!;
+ _variables!.recordDecoratedElementType(
classElement, DecoratedType(classElement.thisType, _graph.never));
- makeNonNullNode(NullabilityNodeTarget target, [AstNode forNode]) {
+ makeNonNullNode(NullabilityNodeTarget target, [AstNode? forNode]) {
forNode ??= node;
final graphNode = NullabilityNode.forInferredType(target);
_graph.makeNonNullableUnion(graphNode, EnumValueOrigin(source, forNode));
@@ -263,15 +261,15 @@
}
for (var item in node.constants) {
- var declaredElement = item.declaredElement;
+ var declaredElement = item.declaredElement!;
var target = NullabilityNodeTarget.element(declaredElement, _getLineInfo);
- _variables.recordDecoratedElementType(declaredElement,
+ _variables!.recordDecoratedElementType(declaredElement,
DecoratedType(classElement.thisType, makeNonNullNode(target, item)));
}
- final valuesGetter = classElement.getGetter('values');
+ final valuesGetter = classElement.getGetter('values')!;
var valuesTarget =
NullabilityNodeTarget.element(valuesGetter, _getLineInfo);
- _variables.recordDecoratedElementType(
+ _variables!.recordDecoratedElementType(
valuesGetter,
DecoratedType(valuesGetter.type, makeNonNullNode(valuesTarget),
returnType: DecoratedType(valuesGetter.returnType,
@@ -280,16 +278,16 @@
DecoratedType(classElement.thisType,
makeNonNullNode(valuesTarget.typeArgument(0)))
])));
- final indexGetter = classElement.getGetter('index');
+ final indexGetter = classElement.getGetter('index')!;
var indexTarget = NullabilityNodeTarget.element(indexGetter, _getLineInfo);
- _variables.recordDecoratedElementType(
+ _variables!.recordDecoratedElementType(
indexGetter,
DecoratedType(indexGetter.type, makeNonNullNode(indexTarget),
returnType: DecoratedType(indexGetter.returnType,
makeNonNullNode(indexTarget.returnType()))));
- final toString = classElement.getMethod('toString');
+ final toString = classElement.getMethod('toString')!;
var toStringTarget = NullabilityNodeTarget.element(toString, _getLineInfo);
- _variables.recordDecoratedElementType(
+ _variables!.recordDecoratedElementType(
toString,
DecoratedType(toString.type, makeNonNullNode(toStringTarget),
returnType: DecoratedType(toString.returnType,
@@ -298,28 +296,28 @@
}
@override
- DecoratedType visitExtensionDeclaration(ExtensionDeclaration node) {
+ DecoratedType? visitExtensionDeclaration(ExtensionDeclaration node) {
node.metadata.accept(this);
node.typeParameters?.accept(this);
var type = _pushNullabilityNodeTarget(
NullabilityNodeTarget.text('extended type'),
() => node.extendedType.accept(this));
- _variables.recordDecoratedElementType(node.declaredElement, type);
+ _variables!.recordDecoratedElementType(node.declaredElement, type);
node.members.accept(this);
return null;
}
@override
- DecoratedType visitFieldFormalParameter(FieldFormalParameter node) {
+ DecoratedType? visitFieldFormalParameter(FieldFormalParameter node) {
return _handleFormalParameter(
node, node.type, node.typeParameters, node.parameters);
}
@override
- DecoratedType visitFormalParameterList(FormalParameterList node) {
+ DecoratedType? visitFormalParameterList(FormalParameterList node) {
int index = 0;
for (var parameter in node.parameters) {
- var element = parameter.declaredElement;
+ var element = parameter.declaredElement!;
NullabilityNodeTarget newTarget;
if (element.isNamed) {
newTarget = safeTarget.namedParameter(element.name);
@@ -332,10 +330,10 @@
}
@override
- DecoratedType visitFunctionDeclaration(FunctionDeclaration node) {
+ DecoratedType? visitFunctionDeclaration(FunctionDeclaration node) {
_handleExecutableDeclaration(
node,
- node.declaredElement,
+ node.declaredElement!,
node.metadata,
node.returnType,
node.functionExpression.typeParameters,
@@ -347,31 +345,31 @@
}
@override
- DecoratedType visitFunctionExpression(FunctionExpression node) {
- _handleExecutableDeclaration(node, node.declaredElement, null, null,
+ DecoratedType? visitFunctionExpression(FunctionExpression node) {
+ _handleExecutableDeclaration(node, node.declaredElement!, null, null,
node.typeParameters, node.parameters, null, node.body, null);
return null;
}
@override
- DecoratedType visitFunctionExpressionInvocation(
+ DecoratedType? visitFunctionExpressionInvocation(
FunctionExpressionInvocation node) {
- node.function?.accept(this);
+ node.function.accept(this);
_pushNullabilityNodeTarget(NullabilityNodeTarget.text('type argument'),
() => node.typeArguments?.accept(this));
- node.argumentList?.accept(this);
+ node.argumentList.accept(this);
return null;
}
@override
- DecoratedType visitFunctionTypeAlias(FunctionTypeAlias node) {
+ DecoratedType? visitFunctionTypeAlias(FunctionTypeAlias node) {
node.metadata.accept(this);
- var declaredElement = node.declaredElement;
+ var declaredElement = node.declaredElement!;
var functionElement =
declaredElement.aliasedElement as GenericFunctionTypeElement;
var functionType = functionElement.type;
var returnType = node.returnType;
- DecoratedType decoratedReturnType;
+ DecoratedType? decoratedReturnType;
var target = NullabilityNodeTarget.element(declaredElement, _getLineInfo);
if (returnType != null) {
_pushNullabilityNodeTarget(target.returnType(), () {
@@ -390,7 +388,7 @@
DecoratedType decoratedFunctionType;
try {
node.typeParameters?.accept(this);
- _pushNullabilityNodeTarget(target, () => node.parameters?.accept(this));
+ _pushNullabilityNodeTarget(target, () => node.parameters.accept(this));
// Note: we don't pass _typeFormalBounds into DecoratedType because we're
// not defining a generic function type, we're defining a generic typedef
// of an ordinary (non-generic) function type.
@@ -402,44 +400,44 @@
_positionalParameters = previousPositionalParameters;
_namedParameters = previousNamedParameters;
}
- _variables.recordDecoratedElementType(
- functionElement, decoratedFunctionType);
+ _variables!
+ .recordDecoratedElementType(functionElement, decoratedFunctionType);
return null;
}
@override
- DecoratedType visitFunctionTypedFormalParameter(
+ DecoratedType? visitFunctionTypedFormalParameter(
FunctionTypedFormalParameter node) {
return _handleFormalParameter(
node, node.returnType, node.typeParameters, node.parameters);
}
@override
- DecoratedType visitGenericTypeAlias(GenericTypeAlias node) {
+ DecoratedType? visitGenericTypeAlias(GenericTypeAlias node) {
node.metadata.accept(this);
- DecoratedType decoratedFunctionType;
+ DecoratedType? decoratedFunctionType;
node.typeParameters?.accept(this);
var target =
- NullabilityNodeTarget.element(node.declaredElement, _getLineInfo);
+ NullabilityNodeTarget.element(node.declaredElement!, _getLineInfo);
_pushNullabilityNodeTarget(target, () {
- decoratedFunctionType = node.functionType.accept(this);
+ decoratedFunctionType = node.functionType!.accept(this);
});
- _variables.recordDecoratedElementType(
+ _variables!.recordDecoratedElementType(
(node.declaredElement as TypeAliasElement).aliasedElement,
decoratedFunctionType);
return null;
}
@override
- DecoratedType visitIsExpression(IsExpression node) {
- node.expression?.accept(this);
+ DecoratedType? visitIsExpression(IsExpression node) {
+ node.expression.accept(this);
_pushNullabilityNodeTarget(NullabilityNodeTarget.text('tested type'),
- () => node.type?.accept(this));
+ () => node.type.accept(this));
return null;
}
@override
- DecoratedType visitListLiteral(ListLiteral node) {
+ DecoratedType? visitListLiteral(ListLiteral node) {
_pushNullabilityNodeTarget(NullabilityNodeTarget.text('list element type'),
() => node.typeArguments?.accept(this));
node.elements.accept(this);
@@ -447,10 +445,10 @@
}
@override
- DecoratedType visitMethodDeclaration(MethodDeclaration node) {
+ DecoratedType? visitMethodDeclaration(MethodDeclaration node) {
_handleExecutableDeclaration(
node,
- node.declaredElement,
+ node.declaredElement!,
node.metadata,
node.returnType,
node.typeParameters,
@@ -462,28 +460,28 @@
}
@override
- DecoratedType visitMethodInvocation(MethodInvocation node) {
+ DecoratedType? visitMethodInvocation(MethodInvocation node) {
node.target?.accept(this);
- node.methodName?.accept(this);
+ node.methodName.accept(this);
_pushNullabilityNodeTarget(NullabilityNodeTarget.text('type argument'),
() => node.typeArguments?.accept(this));
- node.argumentList?.accept(this);
+ node.argumentList.accept(this);
return null;
}
@override
- DecoratedType visitMixinDeclaration(MixinDeclaration node) {
+ DecoratedType? visitMixinDeclaration(MixinDeclaration node) {
node.metadata.accept(this);
- node.name?.accept(this);
+ node.name.accept(this);
node.typeParameters?.accept(this);
node.members.accept(this);
- _handleSupertypeClauses(node, node.declaredElement, null, null,
+ _handleSupertypeClauses(node, node.declaredElement!, null, null,
node.implementsClause, node.onClause);
return null;
}
@override
- DecoratedType visitSetOrMapLiteral(SetOrMapLiteral node) {
+ DecoratedType? visitSetOrMapLiteral(SetOrMapLiteral node) {
var typeArguments = node.typeArguments;
if (typeArguments != null) {
var arguments = typeArguments.arguments;
@@ -503,25 +501,25 @@
}
@override
- DecoratedType visitSimpleFormalParameter(SimpleFormalParameter node) {
+ DecoratedType? visitSimpleFormalParameter(SimpleFormalParameter node) {
return _handleFormalParameter(node, node.type, null, null);
}
@override
DecoratedType visitTypeAnnotation(TypeAnnotation node) {
- assert(node != null); // TODO(paulberry)
- var type = node.type;
+ var type = node.type!;
var target = safeTarget.withCodeRef(node);
if (type.isVoid || type.isDynamic) {
var nullabilityNode = NullabilityNode.forTypeAnnotation(target);
var decoratedType = DecoratedType(type, nullabilityNode);
- _variables.recordDecoratedTypeAnnotation(source, node, decoratedType);
+ _variables!.recordDecoratedTypeAnnotation(source, node, decoratedType);
return decoratedType;
}
var typeArguments = const <DecoratedType>[];
- DecoratedType decoratedReturnType;
- var positionalParameters = const <DecoratedType>[];
- var namedParameters = const <String, DecoratedType>{};
+ DecoratedType? decoratedReturnType;
+ List<DecoratedType?> positionalParameters = const <DecoratedType>[];
+ Map<String, DecoratedType?> namedParameters =
+ const <String, DecoratedType>{};
if (type is InterfaceType && type.element.typeParameters.isNotEmpty) {
if (node is TypeName) {
if (node.typeArguments == null) {
@@ -533,9 +531,9 @@
instrumentation?.implicitTypeArguments(source, node, typeArguments);
} else {
int index = 0;
- typeArguments = node.typeArguments.arguments
+ typeArguments = node.typeArguments!.arguments
.map((t) => _pushNullabilityNodeTarget(
- target.typeArgument(index++), () => t.accept(this)))
+ target.typeArgument(index++), () => t.accept(this)!))
.toList();
}
} else {
@@ -556,8 +554,8 @@
decoratedReturnType = returnType.accept(this);
});
}
- positionalParameters = <DecoratedType>[];
- namedParameters = <String, DecoratedType>{};
+ positionalParameters = <DecoratedType?>[];
+ namedParameters = <String, DecoratedType?>{};
var previousPositionalParameters = _positionalParameters;
var previousNamedParameters = _namedParameters;
try {
@@ -598,7 +596,7 @@
positionalParameters: positionalParameters,
namedParameters: namedParameters);
}
- _variables.recordDecoratedTypeAnnotation(source, node, decoratedType);
+ _variables!.recordDecoratedTypeAnnotation(source, node, decoratedType);
_handleNullabilityHint(node, decoratedType);
return decoratedType;
}
@@ -610,10 +608,10 @@
}
@override
- DecoratedType visitTypeParameter(TypeParameter node) {
- var element = node.declaredElement;
+ DecoratedType? visitTypeParameter(TypeParameter node) {
+ var element = node.declaredElement!;
var bound = node.bound;
- DecoratedType decoratedBound;
+ DecoratedType? decoratedBound;
var target = NullabilityNodeTarget.typeParameterBound(element);
if (bound != null) {
decoratedBound =
@@ -624,43 +622,43 @@
_graph.connect(_graph.always, nullabilityNode,
AlwaysNullableTypeOrigin.forElement(element, false));
}
- DecoratedTypeParameterBounds.current.put(element, decoratedBound);
+ DecoratedTypeParameterBounds.current!.put(element, decoratedBound);
return null;
}
@override
- DecoratedType visitVariableDeclarationList(VariableDeclarationList node) {
+ DecoratedType? visitVariableDeclarationList(VariableDeclarationList node) {
node.metadata.accept(this);
var typeAnnotation = node.type;
var type = _pushNullabilityNodeTarget(
NullabilityNodeTarget.element(
- node.variables.first.declaredElement, _getLineInfo),
+ node.variables.first.declaredElement!, _getLineInfo),
() => typeAnnotation?.accept(this));
var hint = getPrefixHint(node.firstTokenAfterCommentAndMetadata);
if (hint != null && hint.kind == HintCommentKind.late_) {
- _variables.recordLateHint(source, node, hint);
+ _variables!.recordLateHint(source, node, hint);
}
if (hint != null && hint.kind == HintCommentKind.lateFinal) {
- _variables.recordLateHint(source, node, hint);
+ _variables!.recordLateHint(source, node, hint);
}
for (var variable in node.variables) {
variable.metadata.accept(this);
var declaredElement = variable.declaredElement;
if (type == null) {
var target =
- NullabilityNodeTarget.element(declaredElement, _getLineInfo);
+ NullabilityNodeTarget.element(declaredElement!, _getLineInfo);
type = DecoratedType.forImplicitType(
_typeProvider, declaredElement.type, _graph, target);
instrumentation?.implicitType(source, node, type);
}
- _variables.recordDecoratedElementType(declaredElement, type);
+ _variables!.recordDecoratedElementType(declaredElement, type);
variable.initializer?.accept(this);
}
return null;
}
DecoratedType _createDecoratedTypeForClass(
- ClassElement classElement, AstNode node) {
+ ClassElement classElement, AstNode? node) {
var typeArguments = classElement.typeParameters
.map((t) => t.instantiate(nullabilitySuffix: NullabilitySuffix.star))
.toList();
@@ -680,16 +678,16 @@
void _handleExecutableDeclaration(
AstNode node,
ExecutableElement declaredElement,
- NodeList<Annotation> metadata,
- TypeAnnotation returnType,
- TypeParameterList typeParameters,
- FormalParameterList parameters,
- NodeList<ConstructorInitializer> initializers,
+ NodeList<Annotation>? metadata,
+ TypeAnnotation? returnType,
+ TypeParameterList? typeParameters,
+ FormalParameterList? parameters,
+ NodeList<ConstructorInitializer>? initializers,
FunctionBody body,
- ConstructorName redirectedConstructor) {
+ ConstructorName? redirectedConstructor) {
metadata?.accept(this);
var functionType = declaredElement.type;
- DecoratedType decoratedReturnType;
+ DecoratedType? decoratedReturnType;
var target = NullabilityNodeTarget.element(declaredElement, _getLineInfo);
if (returnType != null) {
_pushNullabilityNodeTarget(target.returnType(), () {
@@ -699,7 +697,7 @@
// Constructors have no explicit return type annotation, so use the
// implicit return type.
decoratedReturnType = _createDecoratedTypeForClass(
- declaredElement.enclosingElement, parameters.parent);
+ declaredElement.enclosingElement, parameters!.parent);
instrumentation?.implicitReturnType(source, node, decoratedReturnType);
} else {
// Inferred return type.
@@ -721,23 +719,23 @@
returnType: decoratedReturnType,
positionalParameters: _positionalParameters,
namedParameters: _namedParameters);
- body?.accept(this);
+ body.accept(this);
} finally {
_positionalParameters = previousPositionalParameters;
_namedParameters = previousNamedParameters;
}
- _variables.recordDecoratedElementType(
- declaredElement, decoratedFunctionType);
+ _variables!
+ .recordDecoratedElementType(declaredElement, decoratedFunctionType);
}
- DecoratedType _handleFormalParameter(
+ DecoratedType? _handleFormalParameter(
FormalParameter node,
- TypeAnnotation type,
- TypeParameterList typeParameters,
- FormalParameterList parameters) {
- var declaredElement = node.declaredElement;
- node.metadata?.accept(this);
- DecoratedType decoratedType;
+ TypeAnnotation? type,
+ TypeParameterList? typeParameters,
+ FormalParameterList? parameters) {
+ var declaredElement = node.declaredElement!;
+ node.metadata.accept(this);
+ DecoratedType? decoratedType;
var target = safeTarget;
if (parameters == null) {
if (type != null) {
@@ -748,7 +746,7 @@
instrumentation?.implicitType(source, node, decoratedType);
}
} else {
- DecoratedType decoratedReturnType;
+ DecoratedType? decoratedReturnType;
if (type == null) {
decoratedReturnType = DecoratedType.forImplicitType(_typeProvider,
DynamicTypeImpl.instance, _graph, target.returnType());
@@ -761,8 +759,8 @@
_unimplemented(
typeParameters, 'Function-typed parameter with type parameters');
}
- var positionalParameters = <DecoratedType>[];
- var namedParameters = <String, DecoratedType>{};
+ var positionalParameters = <DecoratedType?>[];
+ var namedParameters = <String, DecoratedType?>{};
var previousPositionalParameters = _positionalParameters;
var previousNamedParameters = _namedParameters;
try {
@@ -780,11 +778,11 @@
namedParameters: namedParameters);
_handleNullabilityHint(node, decoratedType);
}
- _variables.recordDecoratedElementType(declaredElement, decoratedType);
+ _variables!.recordDecoratedElementType(declaredElement, decoratedType);
if (declaredElement.isNamed) {
- _namedParameters[declaredElement.name] = decoratedType;
+ _namedParameters![declaredElement.name] = decoratedType;
} else {
- _positionalParameters.add(decoratedType);
+ _positionalParameters!.add(decoratedType);
}
return decoratedType;
}
@@ -800,29 +798,30 @@
if (hint != null) {
switch (hint.kind) {
case HintCommentKind.bang:
- _graph.makeNonNullableUnion(decoratedType.node,
+ _graph.makeNonNullableUnion(decoratedType.node!,
NullabilityCommentOrigin(source, node, false));
- _variables.recordNullabilityHint(source, node, hint);
- decoratedType.node.hintActions[HintActionKind.removeNonNullableHint] =
- hint.changesToRemove(source.contents.data);
- decoratedType.node.hintActions[HintActionKind.changeToNullableHint] =
- hint.changesToReplace(source.contents.data, '/*?*/');
+ _variables!.recordNullabilityHint(source, node, hint);
+ decoratedType
+ .node!.hintActions[HintActionKind.removeNonNullableHint] =
+ hint.changesToRemove(source!.contents.data);
+ decoratedType.node!.hintActions[HintActionKind.changeToNullableHint] =
+ hint.changesToReplace(source!.contents.data, '/*?*/');
break;
case HintCommentKind.question:
- _graph.makeNullableUnion(
- decoratedType.node, NullabilityCommentOrigin(source, node, true));
- _variables.recordNullabilityHint(source, node, hint);
- decoratedType.node.hintActions[HintActionKind.removeNullableHint] =
- hint.changesToRemove(source.contents.data);
+ _graph.makeNullableUnion(decoratedType.node!,
+ NullabilityCommentOrigin(source, node, true));
+ _variables!.recordNullabilityHint(source, node, hint);
+ decoratedType.node!.hintActions[HintActionKind.removeNullableHint] =
+ hint.changesToRemove(source!.contents.data);
decoratedType
- .node.hintActions[HintActionKind.changeToNonNullableHint] =
- hint.changesToReplace(source.contents.data, '/*!*/');
+ .node!.hintActions[HintActionKind.changeToNonNullableHint] =
+ hint.changesToReplace(source!.contents.data, '/*!*/');
break;
default:
break;
}
- decoratedType.node.hintActions
+ decoratedType.node!.hintActions
..remove(HintActionKind.addNonNullableHint)
..remove(HintActionKind.addNullableHint);
}
@@ -831,11 +830,11 @@
void _handleSupertypeClauses(
NamedCompilationUnitMember astNode,
ClassElement declaredElement,
- TypeName superclass,
- WithClause withClause,
- ImplementsClause implementsClause,
- OnClause onClause) {
- var supertypes = <TypeName>[];
+ TypeName? superclass,
+ WithClause? withClause,
+ ImplementsClause? implementsClause,
+ OnClause? onClause) {
+ var supertypes = <TypeName?>[];
supertypes.add(superclass);
if (withClause != null) {
supertypes.addAll(withClause.mixinTypes);
@@ -846,15 +845,15 @@
if (onClause != null) {
supertypes.addAll(onClause.superclassConstraints);
}
- var decoratedSupertypes = <ClassElement, DecoratedType>{};
+ var decoratedSupertypes = <ClassElement, DecoratedType?>{};
_pushNullabilityNodeTarget(
NullabilityNodeTarget.element(declaredElement, _getLineInfo).supertype,
() {
for (var supertype in supertypes) {
- DecoratedType decoratedSupertype;
+ DecoratedType? decoratedSupertype;
if (supertype == null) {
var nullabilityNode =
- NullabilityNode.forInferredType(_target.withCodeRef(astNode));
+ NullabilityNode.forInferredType(_target!.withCodeRef(astNode));
_graph.makeNonNullableUnion(
nullabilityNode, NonNullableObjectSuperclass(source, astNode));
decoratedSupertype =
@@ -862,17 +861,17 @@
} else {
decoratedSupertype = supertype.accept(this);
}
- var class_ = (decoratedSupertype.type as InterfaceType).element;
+ var class_ = (decoratedSupertype!.type as InterfaceType).element;
decoratedSupertypes[class_] = decoratedSupertype;
}
});
- _variables.recordDecoratedDirectSupertypes(
- declaredElement, decoratedSupertypes);
+ _variables!
+ .recordDecoratedDirectSupertypes(declaredElement, decoratedSupertypes);
}
T _pushNullabilityNodeTarget<T>(
NullabilityNodeTarget target, T Function() fn) {
- NullabilityNodeTarget previousTarget = _target;
+ NullabilityNodeTarget? previousTarget = _target;
try {
_target = target;
return fn();
@@ -889,9 +888,9 @@
buffer.write(' in "');
buffer.write(node.toSource());
buffer.write('" on line ');
- buffer.write(unit.lineInfo.getLocation(node.offset).lineNumber);
+ buffer.write(unit.lineInfo!.getLocation(node.offset).lineNumber);
buffer.write(' of "');
- buffer.write(unit.declaredElement.source.fullName);
+ buffer.write(unit.declaredElement!.source.fullName);
buffer.write('"');
throw UnimplementedError(buffer.toString());
}
diff --git a/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart b/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart
index 1738fe8..07c652a 100644
--- a/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart
+++ b/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart
@@ -25,26 +25,26 @@
/// Implementation of the [NullabilityMigration] public API.
class NullabilityMigrationImpl implements NullabilityMigration {
- final NullabilityMigrationListener listener;
+ final NullabilityMigrationListener? listener;
- Variables _variables;
+ Variables? _variables;
final NullabilityGraph _graph;
- final bool _permissive;
+ final bool? _permissive;
- final NullabilityMigrationInstrumentation _instrumentation;
+ final NullabilityMigrationInstrumentation? _instrumentation;
- DecoratedClassHierarchy _decoratedClassHierarchy;
+ DecoratedClassHierarchy? _decoratedClassHierarchy;
bool _propagated = false;
/// Indicates whether code removed by the migration engine should be removed
/// by commenting it out. A value of `false` means to actually delete the
/// code that is removed.
- final bool removeViaComments;
+ final bool? removeViaComments;
- final bool warnOnWeakCode;
+ final bool? warnOnWeakCode;
final _decoratedTypeParameterBounds = DecoratedTypeParameterBounds();
@@ -75,12 +75,12 @@
/// Optional parameter [warnOnWeakCode] indicates whether weak-only code
/// should be warned about or removed (in the way specified by
/// [removeViaComments]).
- NullabilityMigrationImpl(NullabilityMigrationListener listener,
+ NullabilityMigrationImpl(NullabilityMigrationListener? listener,
LineInfo Function(String) getLineInfo,
- {bool permissive = false,
- NullabilityMigrationInstrumentation instrumentation,
- bool removeViaComments = false,
- bool warnOnWeakCode = true})
+ {bool? permissive = false,
+ NullabilityMigrationInstrumentation? instrumentation,
+ bool? removeViaComments = false,
+ bool? warnOnWeakCode = true})
: this._(
listener,
NullabilityGraph(instrumentation: instrumentation),
@@ -102,7 +102,7 @@
}
@override
- bool get isPermissive => _permissive;
+ bool? get isPermissive => _permissive;
@override
List<String> get unmigratedDependencies {
@@ -123,7 +123,7 @@
@override
void finalizeInput(ResolvedUnitResult result) {
- if (result.unit.featureSet.isEnabled(Feature.non_nullable)) {
+ if (result.unit!.featureSet.isEnabled(Feature.non_nullable)) {
// This library has already been migrated; nothing more to do.
return;
}
@@ -132,8 +132,8 @@
_propagated = true;
_graph.propagate();
}
- var unit = result.unit;
- var compilationUnit = unit.declaredElement;
+ var unit = result.unit!;
+ var compilationUnit = unit.declaredElement!;
var library = compilationUnit.library;
var source = compilationUnit.source;
// Hierarchies were created assuming the libraries being migrated are opted
@@ -147,7 +147,7 @@
library.typeSystem as TypeSystemImpl,
_variables,
library,
- _permissive ? listener : null,
+ _permissive! ? listener : null,
unit,
warnOnWeakCode,
_graph,
@@ -159,22 +159,22 @@
DecoratedTypeParameterBounds.current = null;
}
var changes = FixAggregator.run(unit, result.content, fixBuilder.changes,
- removeViaComments: removeViaComments, warnOnWeakCode: warnOnWeakCode);
+ removeViaComments: removeViaComments, warnOnWeakCode: warnOnWeakCode)!;
_instrumentation?.changes(source, changes);
final lineInfo = LineInfo.fromContent(source.contents.data);
var offsets = changes.keys.toList();
offsets.sort();
for (var offset in offsets) {
- var edits = changes[offset];
+ var edits = changes[offset]!;
var descriptions = edits
.map((edit) => edit.info)
.where((info) => info != null)
- .map((info) => info.description.appliedMessage)
+ .map((info) => info!.description.appliedMessage)
.join(', ');
- var sourceEdit = edits.toSourceEdit(offset);
- listener.addSuggestion(
+ var sourceEdit = edits.toSourceEdit(offset!);
+ listener!.addSuggestion(
descriptions, _computeLocation(lineInfo, sourceEdit, source));
- listener.addEdit(source, sourceEdit);
+ listener!.addEdit(source, sourceEdit);
}
}
@@ -188,7 +188,7 @@
!_queriedUnmigratedDependencies,
'Should only query unmigratedDependencies after all calls to '
'prepareInput');
- if (result.unit.featureSet.isEnabled(Feature.non_nullable)) {
+ if (result.unit!.featureSet.isEnabled(Feature.non_nullable)) {
// This library has already been migrated; nothing more to do.
return;
}
@@ -202,13 +202,13 @@
instrumentation: _instrumentation);
_decoratedClassHierarchy = DecoratedClassHierarchy(_variables, _graph);
}
- var unit = result.unit;
+ var unit = result.unit!;
try {
DecoratedTypeParameterBounds.current = _decoratedTypeParameterBounds;
unit.accept(NodeBuilder(
_variables,
- unit.declaredElement.source,
- _permissive ? listener : null,
+ unit.declaredElement!.source,
+ _permissive! ? listener : null,
_graph,
result.typeProvider,
_getLineInfo,
@@ -219,12 +219,12 @@
}
void processInput(ResolvedUnitResult result) {
- if (result.unit.featureSet.isEnabled(Feature.non_nullable)) {
+ if (result.unit!.featureSet.isEnabled(Feature.non_nullable)) {
// This library has already been migrated; nothing more to do.
return;
}
ExperimentStatusException.sanityCheck(result);
- var unit = result.unit;
+ var unit = result.unit!;
try {
DecoratedTypeParameterBounds.current = _decoratedTypeParameterBounds;
unit.accept(EdgeBuilder(
@@ -232,8 +232,8 @@
result.typeSystem,
_variables,
_graph,
- unit.declaredElement.source,
- _permissive ? listener : null,
+ unit.declaredElement!.source,
+ _permissive! ? listener : null,
_decoratedClassHierarchy,
instrumentation: _instrumentation));
} finally {
diff --git a/pkg/nnbd_migration/lib/src/nullability_node.dart b/pkg/nnbd_migration/lib/src/nullability_node.dart
index d3da196..8030833 100644
--- a/pkg/nnbd_migration/lib/src/nullability_node.dart
+++ b/pkg/nnbd_migration/lib/src/nullability_node.dart
@@ -21,7 +21,7 @@
abstract class DownstreamPropagationStep extends PropagationStep
implements DownstreamPropagationStepInfo {
@override
- NullabilityNodeMutable targetNode;
+ NullabilityNodeMutable? targetNode;
/// The state that the node's nullability was changed to.
///
@@ -29,12 +29,12 @@
/// Propagation steps that are pending but have not taken effect yet, or that
/// never had an effect (e.g. because an edge was not triggered) will have a
/// `null` value for this field.
- Nullability newState;
+ Nullability? newState;
DownstreamPropagationStep();
@override
- DownstreamPropagationStep get principalCause;
+ DownstreamPropagationStep? get principalCause;
}
/// Base class for steps that occur as part of propagating exact nullability
@@ -74,28 +74,28 @@
/// A set of upstream nodes. By convention, the first node is the source node
/// and the other nodes are "guards". The destination node will only need to
/// be made nullable if all the upstream nodes are nullable.
- final List<NullabilityNode> upstreamNodes;
+ final List<NullabilityNode?> upstreamNodes;
final _NullabilityEdgeKind _kind;
/// The location in the source code that caused this edge to be built.
- final CodeReference codeReference;
+ final CodeReference? codeReference;
final String description;
/// Whether this edge is the result of an uninitialized variable declaration.
- final bool isUninit;
+ final bool? isUninit;
/// Whether this edge is the result of an assignment within the test package's
/// `setUp` function.
- final bool isSetupAssignment;
+ final bool? isSetupAssignment;
NullabilityEdge._(
this.destinationNode, this.upstreamNodes, this._kind, this.description,
{this.codeReference, this.isUninit, this.isSetupAssignment});
@override
- Iterable<NullabilityNode> get guards => upstreamNodes.skip(1);
+ Iterable<NullabilityNode?> get guards => upstreamNodes.skip(1);
/// Indicates whether it's possible for migration to cope with this edge being
/// unsatisfied by inserting a null check. Graph propagation favors
@@ -118,7 +118,7 @@
@override
bool get isTriggered {
for (var upstreamNode in upstreamNodes) {
- if (!upstreamNode.isNullable) return false;
+ if (!upstreamNode!.isNullable) return false;
}
return true;
}
@@ -133,11 +133,11 @@
}
@override
- NullabilityNode get sourceNode => upstreamNodes.first;
+ NullabilityNode? get sourceNode => upstreamNodes.first;
@override
- String toString({NodeToIdMapper idMapper}) {
- var edgeDecorations = <Object>[];
+ String toString({NodeToIdMapper? idMapper}) {
+ var edgeDecorations = <Object?>[];
switch (_kind) {
case _NullabilityEdgeKind.soft:
break;
@@ -160,7 +160,7 @@
edgeDecorations.addAll(guards);
var edgeDecoration =
edgeDecorations.isEmpty ? '' : '-(${edgeDecorations.join(', ')})';
- return '${sourceNode.toString(idMapper: idMapper)} $edgeDecoration-> '
+ return '${sourceNode!.toString(idMapper: idMapper)} $edgeDecoration-> '
'${destinationNode.toString(idMapper: idMapper)}';
}
}
@@ -176,7 +176,7 @@
/// propagation.
static const _debugAfterPropagation = false;
- final NullabilityMigrationInstrumentation /*?*/ instrumentation;
+ final NullabilityMigrationInstrumentation? instrumentation;
/// Returns a [NullabilityNode] that is a priori nullable.
///
@@ -197,7 +197,7 @@
final _pathsBeingMigrated = <String>{};
/// A set containing all of the nodes in the graph.
- final Set<NullabilityNode> nodes = {};
+ final Set<NullabilityNode?> nodes = {};
NullabilityGraph({this.instrumentation})
: always = _NullabilityNodeImmutable('always', true),
@@ -206,11 +206,11 @@
/// Records that [sourceNode] is immediately upstream from [destinationNode].
///
/// Returns the edge created by the connection.
- NullabilityEdge connect(NullabilityNode sourceNode,
+ NullabilityEdge connect(NullabilityNode? sourceNode,
NullabilityNode destinationNode, EdgeOrigin origin,
{bool hard = false,
bool checkable = true,
- List<NullabilityNode> guards = const []}) {
+ List<NullabilityNode?> guards = const []}) {
var upstreamNodes = [sourceNode, ...guards];
var kind = hard
? checkable
@@ -224,13 +224,14 @@
/// Records that [sourceNode] is immediately upstream from [always], via a
/// dummy edge.
- NullabilityEdge connectDummy(NullabilityNode sourceNode, EdgeOrigin origin) =>
+ NullabilityEdge connectDummy(
+ NullabilityNode? sourceNode, EdgeOrigin origin) =>
_connect([sourceNode], always, _NullabilityEdgeKind.dummy, origin);
/// Prints out a representation of the graph nodes. Useful in debugging
/// broken tests.
void debugDump() {
- Set<NullabilityNode> visitedNodes = {};
+ Set<NullabilityNode?> visitedNodes = {};
Map<NullabilityNode, String> shortNames = {};
int counter = 0;
String nameNode(NullabilityNode node) {
@@ -249,16 +250,16 @@
print(' $name [label="$label"$styleSuffix]');
if (node is NullabilityNodeCompound) {
for (var component in node._components) {
- print(' ${nameNode(component)} -> $name [style=dashed]');
+ print(' ${nameNode(component!)} -> $name [style=dashed]');
}
}
}
return name;
}
- void visitNode(NullabilityNode node) {
+ void visitNode(NullabilityNode? node) {
if (!visitedNodes.add(node)) return;
- for (var edge in node._upstreamEdges) {
+ for (var edge in node!._upstreamEdges) {
String suffix;
if (edge.isUnion) {
suffix = ' [label="union"]';
@@ -272,13 +273,13 @@
var upstreamNodes = edge.upstreamNodes;
if (upstreamNodes.length == 1) {
print(
- ' ${nameNode(upstreamNodes.single)} -> ${nameNode(node)}$suffix');
+ ' ${nameNode(upstreamNodes.single!)} -> ${nameNode(node)}$suffix');
} else {
var tmpName = 'n${counter++}';
print(' $tmpName [label=""]');
print(' $tmpName -> ${nameNode(node)}$suffix}');
for (var upstreamNode in upstreamNodes) {
- print(' ${nameNode(upstreamNode)} -> $tmpName');
+ print(' ${nameNode(upstreamNode!)} -> $tmpName');
}
}
}
@@ -305,8 +306,8 @@
/// Creates a graph edge that will try to force the given [node] to be
/// non-nullable.
- NullabilityEdge makeNonNullable(NullabilityNode node, EdgeOrigin origin,
- {bool hard = true, List<NullabilityNode> guards = const []}) {
+ NullabilityEdge makeNonNullable(NullabilityNode? node, EdgeOrigin origin,
+ {bool hard = true, List<NullabilityNode?> guards = const []}) {
return connect(node, never, origin, hard: hard, guards: guards);
}
@@ -361,12 +362,12 @@
// we need to traverse the graph from both directions.
//
for (var node in nodes) {
- node.resetState();
+ node!.resetState();
}
//
// Reset the state of the listener.
//
- instrumentation.prepareForUpdate();
+ instrumentation!.prepareForUpdate();
//
// Re-run the propagation step.
//
@@ -374,23 +375,23 @@
}
NullabilityEdge _connect(
- List<NullabilityNode> upstreamNodes,
+ List<NullabilityNode?> upstreamNodes,
NullabilityNode destinationNode,
_NullabilityEdgeKind kind,
EdgeOrigin origin) {
- var isUninit = origin?.kind == EdgeOriginKind.fieldNotInitialized ||
- origin?.kind == EdgeOriginKind.implicitNullInitializer ||
- origin?.kind == EdgeOriginKind.uninitializedRead;
+ var isUninit = origin.kind == EdgeOriginKind.fieldNotInitialized ||
+ origin.kind == EdgeOriginKind.implicitNullInitializer ||
+ origin.kind == EdgeOriginKind.uninitializedRead;
var isSetupAssignment =
origin is ExpressionChecksOrigin && origin.isSetupAssignment;
var edge = NullabilityEdge._(
- destinationNode, upstreamNodes, kind, origin?.description,
- codeReference: origin?.codeReference,
+ destinationNode, upstreamNodes, kind, origin.description,
+ codeReference: origin.codeReference,
isUninit: isUninit,
isSetupAssignment: isSetupAssignment);
instrumentation?.graphEdge(edge, origin);
for (var upstreamNode in upstreamNodes) {
- _connectDownstream(upstreamNode, edge);
+ _connectDownstream(upstreamNode!, edge);
}
destinationNode._upstreamEdges.add(edge);
nodes.addAll(upstreamNodes);
@@ -402,7 +403,7 @@
upstreamNode._downstreamEdges.add(edge);
if (upstreamNode is NullabilityNodeCompound) {
for (var component in upstreamNode._components) {
- _connectDownstream(component, edge);
+ _connectDownstream(component!, edge);
}
}
}
@@ -424,11 +425,11 @@
/// Retrieves the [EdgeOrigin] object that was used to create [edge].
@visibleForTesting
- EdgeOrigin getEdgeOrigin(NullabilityEdge edge) => _edgeOrigins[edge];
+ EdgeOrigin? getEdgeOrigin(NullabilityEdge edge) => _edgeOrigins[edge];
@override
NullabilityEdge _connect(
- List<NullabilityNode> upstreamNodes,
+ List<NullabilityNode?> upstreamNodes,
NullabilityNode destinationNode,
_NullabilityEdgeKind kind,
EdgeOrigin origin) {
@@ -449,7 +450,7 @@
LateCondition _lateCondition = LateCondition.notLate;
@override
- final hintActions = <HintActionKind, Map<int, List<AtomicEdit>>>{};
+ final hintActions = <HintActionKind, Map<int?, List<AtomicEdit>>>{};
bool _isPossiblyOptional = false;
@@ -491,8 +492,8 @@
/// Creates a [NullabilityNode] representing the nullability of an
/// expression which is nullable iff either [a] or [b] is nullable.
- factory NullabilityNode.forLUB(NullabilityNode left, NullabilityNode right) =
- NullabilityNodeForLUB._;
+ factory NullabilityNode.forLUB(
+ NullabilityNode? left, NullabilityNode? right) = NullabilityNodeForLUB._;
/// Creates a [NullabilityNode] representing the nullability of a type
/// substitution where [outerNode] is the nullability node for the type
@@ -502,8 +503,8 @@
/// If either [innerNode] or [outerNode] is `null`, then the other node is
/// returned.
factory NullabilityNode.forSubstitution(
- NullabilityNode innerNode, NullabilityNode outerNode) {
- if (innerNode == null) return outerNode;
+ NullabilityNode? innerNode, NullabilityNode? outerNode) {
+ if (innerNode == null) return outerNode!;
if (outerNode == null) return innerNode;
return NullabilityNodeForSubstitution._(innerNode, outerNode);
}
@@ -516,7 +517,7 @@
NullabilityNode._();
@override
- CodeReference get codeReference => null;
+ CodeReference? get codeReference => null;
/// Gets a string that can be appended to a type name during debugging to help
/// annotate the nullability of that type.
@@ -554,14 +555,14 @@
Iterable<EdgeInfo> get upstreamEdges => _upstreamEdges;
@override
- UpstreamPropagationStep get whyNotNullable;
+ UpstreamPropagationStep? get whyNotNullable;
- Nullability get _nullability;
+ Nullability? get _nullability;
/// Records the fact that an invocation was made to a function with named
/// parameters, and the named parameter associated with this node was not
/// supplied.
- void recordNamedParameterNotSupplied(List<NullabilityNode> guards,
+ void recordNamedParameterNotSupplied(List<NullabilityNode?> guards,
NullabilityGraph graph, NamedParameterNotSuppliedOrigin origin) {
if (isPossiblyOptional) {
graph.connect(graph.always, this, origin, guards: guards);
@@ -571,7 +572,7 @@
/// Reset the state of this node to what it was before the graph was solved.
void resetState();
- String toString({NodeToIdMapper idMapper}) {
+ String toString({NodeToIdMapper? idMapper}) {
var name = displayName;
if (idMapper == null) {
return name;
@@ -595,43 +596,43 @@
NullabilityNodeCompound() : super._();
/// A map describing each of the node's components by name.
- Map<String, NullabilityNode> get componentsByName;
+ Map<String, NullabilityNode?> get componentsByName;
@override
- bool get isExactNullable => _components.any((c) => c.isExactNullable);
+ bool get isExactNullable => _components.any((c) => c!.isExactNullable);
@override
- bool get isNullable => _components.any((c) => c.isNullable);
+ bool get isNullable => _components.any((c) => c!.isNullable);
- Iterable<NullabilityNode> get _components;
+ Iterable<NullabilityNode?> get _components;
}
/// Derived class for nullability nodes that arise from the least-upper-bound
/// implied by a conditional expression.
class NullabilityNodeForLUB extends NullabilityNodeCompound {
- final NullabilityNode left;
+ final NullabilityNode? left;
- final NullabilityNode right;
+ final NullabilityNode? right;
NullabilityNodeForLUB._(this.left, this.right) {
- left.outerCompoundNodes.add(this);
- right.outerCompoundNodes.add(this);
+ left!.outerCompoundNodes.add(this);
+ right!.outerCompoundNodes.add(this);
}
@override
- Map<String, NullabilityNode> get componentsByName =>
+ Map<String, NullabilityNode?> get componentsByName =>
{'left': left, 'right': right};
@override
- String get displayName => '${left.displayName} or ${right.displayName}';
+ String get displayName => '${left!.displayName} or ${right!.displayName}';
@override
- Iterable<NullabilityNode> get _components => [left, right];
+ Iterable<NullabilityNode?> get _components => [left, right];
@override
void resetState() {
- left.resetState();
- right.resetState();
+ left!.resetState();
+ right!.resetState();
}
}
@@ -673,13 +674,13 @@
/// Nearly all nullability nodes derive from this class; the only exceptions are
/// the fixed nodes "always "never".
abstract class NullabilityNodeMutable extends NullabilityNode {
- Nullability _nullability;
+ Nullability? _nullability;
NonNullIntent _nonNullIntent;
- DownstreamPropagationStep _whyNullable;
+ DownstreamPropagationStep? _whyNullable;
- UpstreamPropagationStep _whyNotNullable;
+ UpstreamPropagationStep? _whyNotNullable;
NullabilityNodeMutable._(
{Nullability initialNullability = Nullability.nonNullable})
@@ -688,22 +689,22 @@
super._();
@override
- bool get isExactNullable => _nullability.isExactNullable;
+ bool get isExactNullable => _nullability!.isExactNullable;
@override
bool get isImmutable => false;
@override
- bool get isNullable => _nullability.isNullable;
+ bool get isNullable => _nullability!.isNullable;
@override
NonNullIntent get nonNullIntent => _nonNullIntent;
@override
- UpstreamPropagationStep get whyNotNullable => _whyNotNullable;
+ UpstreamPropagationStep? get whyNotNullable => _whyNotNullable;
@override
- DownstreamPropagationStepInfo get whyNullable => _whyNullable;
+ DownstreamPropagationStepInfo? get whyNullable => _whyNullable;
@override
void resetState() {
@@ -731,14 +732,14 @@
/// The location in the source code that caused this step to be necessary,
/// or `null` if not known.
- CodeReference get codeReference => null;
+ CodeReference? get codeReference => null;
/// The previous propagation step that led to this one, or `null` if there was
/// no previous step.
- PropagationStep get principalCause;
+ PropagationStep? get principalCause;
@override
- String toString({NodeToIdMapper idMapper});
+ String toString({NodeToIdMapper? idMapper});
}
/// Propagation step where we consider mark one of the components of a
@@ -754,11 +755,11 @@
ResolveSubstitutionPropagationStep(this.principalCause, this.node);
@override
- EdgeInfo get edge => null;
+ EdgeInfo? get edge => null;
@override
- String toString({NodeToIdMapper idMapper}) =>
- '${targetNode.toString(idMapper: idMapper)} becomes $newState due to '
+ String toString({NodeToIdMapper? idMapper}) =>
+ '${targetNode!.toString(idMapper: idMapper)} becomes $newState due to '
'${node.toString(idMapper: idMapper)}';
}
@@ -766,7 +767,7 @@
/// to its sources becoming nullable.
class SimpleDownstreamPropagationStep extends DownstreamPropagationStep {
@override
- final DownstreamPropagationStep principalCause;
+ final DownstreamPropagationStep? principalCause;
@override
final NullabilityEdge edge;
@@ -774,11 +775,11 @@
SimpleDownstreamPropagationStep(this.principalCause, this.edge);
@override
- CodeReference get codeReference => edge.codeReference;
+ CodeReference? get codeReference => edge.codeReference;
@override
- String toString({NodeToIdMapper idMapper}) =>
- '${targetNode.toString(idMapper: idMapper)} becomes $newState due to '
+ String toString({NodeToIdMapper? idMapper}) =>
+ '${targetNode!.toString(idMapper: idMapper)} becomes $newState due to '
'${edge.toString(idMapper: idMapper)}';
}
@@ -794,8 +795,8 @@
SimpleExactNullablePropagationStep(this.principalCause, this.edge);
@override
- String toString({NodeToIdMapper idMapper}) =>
- '${targetNode.toString(idMapper: idMapper)} becomes $newState due to '
+ String toString({NodeToIdMapper? idMapper}) =>
+ '${targetNode!.toString(idMapper: idMapper)} becomes $newState due to '
'${edge.toString(idMapper: idMapper)}';
}
@@ -804,7 +805,7 @@
class UpstreamPropagationStep extends PropagationStep
implements UpstreamPropagationStepInfo {
@override
- final UpstreamPropagationStep principalCause;
+ final UpstreamPropagationStep? principalCause;
/// The node being marked as having non-null intent.
final NullabilityNode node;
@@ -814,7 +815,7 @@
/// The nullability edge connecting [node] to the node it is upstream from, if
/// any.
- final NullabilityEdge edge;
+ final NullabilityEdge? edge;
@override
final bool isStartingPoint;
@@ -824,10 +825,10 @@
{this.isStartingPoint = false});
@override
- CodeReference get codeReference => edge?.codeReference;
+ CodeReference? get codeReference => edge?.codeReference;
@override
- String toString({NodeToIdMapper idMapper}) =>
+ String toString({NodeToIdMapper? idMapper}) =>
'${node.toString(idMapper: idMapper)} becomes $newNonNullIntent';
}
@@ -872,7 +873,7 @@
String get debugSuffix => isNullable ? '?' : '';
@override
- Map<HintActionKind, Map<int, List<AtomicEdit>>> get hintActions => const {};
+ Map<HintActionKind, Map<int?, List<AtomicEdit>>> get hintActions => const {};
@override
// Note: the node "always" is not exact nullable, because exact nullability is
@@ -889,10 +890,10 @@
isNullable ? NonNullIntent.none : NonNullIntent.direct;
@override
- UpstreamPropagationStep get whyNotNullable => null;
+ UpstreamPropagationStep? get whyNotNullable => null;
@override
- DownstreamPropagationStepInfo get whyNullable => null;
+ DownstreamPropagationStepInfo? get whyNullable => null;
@override
Nullability get _nullability =>
@@ -910,7 +911,7 @@
_NullabilityNodeSimple(this.target) : super._();
@override
- CodeReference get codeReference => target.codeReference;
+ CodeReference? get codeReference => target.codeReference;
@override
String get displayName => target.displayName;
@@ -957,11 +958,11 @@
var edge = step.edge;
if (!edge.isTriggered) continue;
var node = edge.destinationNode;
- if (edge.isUninit && !node.isNullable) {
+ if (edge.isUninit! && !node.isNullable) {
// [edge] is an edge from always to an uninitialized variable
// declaration.
var isSetupAssigned = node.upstreamEdges
- .any((e) => e is NullabilityEdge && e.isSetupAssignment);
+ .any((e) => e is NullabilityEdge && e.isSetupAssignment!);
// Whether all downstream edges go to nodes with non-null intent.
var allDownstreamHaveNonNullIntent = false;
@@ -1053,7 +1054,7 @@
// propagate to it.
for (var node in pendingNode.outerCompoundNodes) {
if (node._components
- .any((component) => !component.nonNullIntent.isPresent)) {
+ .any((component) => !component!.nonNullIntent.isPresent)) {
continue;
}
var oldNonNullIntent = node._nonNullIntent;
@@ -1071,7 +1072,7 @@
void _resolvePendingSubstitution(ResolveSubstitutionPropagationStep step) {
NullabilityNodeForSubstitution substitutionNode = step.node;
- assert(substitutionNode._nullability.isNullable);
+ assert(substitutionNode._nullability!.isNullable);
// If both nodes pointed to by the substitution node have non-null intent,
// then no resolution is needed; the substitution node can’t be satisfied.
if (substitutionNode.innerNode.nonNullIntent.isPresent &&
@@ -1154,9 +1155,9 @@
}
Nullability _setNullable(DownstreamPropagationStep step) {
- var node = step.targetNode;
+ var node = step.targetNode!;
var newState = step.newState;
- var oldState = node._nullability;
+ var oldState = node._nullability!;
node._nullability = newState;
if (!oldState.isNullable) {
node._whyNullable = step;
diff --git a/pkg/nnbd_migration/lib/src/nullability_node_target.dart b/pkg/nnbd_migration/lib/src/nullability_node_target.dart
index 8b0a9c6..da43dbd 100644
--- a/pkg/nnbd_migration/lib/src/nullability_node_target.dart
+++ b/pkg/nnbd_migration/lib/src/nullability_node_target.dart
@@ -7,7 +7,7 @@
import 'package:analyzer/source/line_info.dart';
import 'package:nnbd_migration/instrumentation.dart';
-String _computeElementName(Element element) {
+String _computeElementName(Element? element) {
List<String> parts = [];
while (element != null && element is! CompilationUnitElement) {
var name = element.name;
@@ -45,7 +45,7 @@
/// The source code location associated with this target, if known. Otherwise
/// `null`.
- CodeReference get codeReference => null;
+ CodeReference? get codeReference => null;
/// Gets a short description of this nullability node target suitable for
/// displaying to the user, not including a code reference.
@@ -55,7 +55,7 @@
/// after the description in parentheses.
String get displayName {
if (codeReference == null) return description;
- return '$description (${codeReference.shortName})';
+ return '$description (${codeReference!.shortName})';
}
NullabilityNodeTarget get supertype => _NullabilityNodeTarget_Supertype(this);
@@ -141,7 +141,7 @@
_NullabilityNodeTarget_Part(this.inner) : super._();
@override
- CodeReference get codeReference => inner.codeReference;
+ CodeReference? get codeReference => inner.codeReference;
}
/// Nullability node target representing the type of a positional function
diff --git a/pkg/nnbd_migration/lib/src/potential_modification.dart b/pkg/nnbd_migration/lib/src/potential_modification.dart
index 3111a8e..50ee51a 100644
--- a/pkg/nnbd_migration/lib/src/potential_modification.dart
+++ b/pkg/nnbd_migration/lib/src/potential_modification.dart
@@ -20,7 +20,7 @@
final _KeepNode thenStatement;
- final _KeepNode elseStatement;
+ final _KeepNode? elseStatement;
factory ConditionalModification(AstNode node, ConditionalDiscard discard) {
if (node is IfStatement) {
@@ -31,7 +31,7 @@
discard,
_KeepNode(node.condition),
_KeepNode(node.thenStatement),
- node.elseStatement == null ? null : _KeepNode(node.elseStatement));
+ node.elseStatement == null ? null : _KeepNode(node.elseStatement!));
} else if (node is IfElement) {
return ConditionalModification._(
node.offset,
@@ -40,7 +40,7 @@
discard,
_KeepNode(node.condition),
_KeepNode(node.thenElement),
- node.elseElement == null ? null : _KeepNode(node.elseElement));
+ node.elseElement == null ? null : _KeepNode(node.elseElement!));
} else {
throw UnimplementedError('TODO(paulberry)');
}
@@ -63,8 +63,8 @@
int offset = node.offset;
int end = node.end;
if (node is Block && node.statements.isNotEmpty) {
- offset = node.statements.beginToken.offset;
- end = node.statements.endToken.end;
+ offset = node.statements.beginToken!.offset;
+ end = node.statements.endToken!.end;
}
return _KeepNode._(offset, end, node is Expression);
}
diff --git a/pkg/nnbd_migration/lib/src/preview/dart_file_page.dart b/pkg/nnbd_migration/lib/src/preview/dart_file_page.dart
index b0d5e6c..d1ff55a 100644
--- a/pkg/nnbd_migration/lib/src/preview/dart_file_page.dart
+++ b/pkg/nnbd_migration/lib/src/preview/dart_file_page.dart
@@ -19,7 +19,7 @@
DartFilePage(PreviewSite site, this.unitInfo)
// TODO(brianwilkerson) The path needs to be converted to use '/' if that
// isn't already done as part of building the unitInfo.
- : super(site, unitInfo.path.substring(1));
+ : super(site, unitInfo.path!.substring(1));
@override
bool get requiresAuth => true;
diff --git a/pkg/nnbd_migration/lib/src/preview/http_preview_server.dart b/pkg/nnbd_migration/lib/src/preview/http_preview_server.dart
index c817e97..d27ac3d 100644
--- a/pkg/nnbd_migration/lib/src/preview/http_preview_server.dart
+++ b/pkg/nnbd_migration/lib/src/preview/http_preview_server.dart
@@ -27,10 +27,10 @@
final MigrationState migrationState;
/// The [PreviewSite] that can handle GET and POST requests.
- PreviewSite _previewSite;
+ PreviewSite? _previewSite;
/// Future that is completed with the HTTP server once it is running.
- Future<HttpServer> _serverFuture;
+ Future<HttpServer>? _serverFuture;
// A function which allows the migration to be rerun, taking changed paths.
final Future<MigrationState> Function() rerunFunction;
@@ -42,11 +42,11 @@
/// The internet address the server should bind to. Should be suitable for
/// passing to HttpServer.bind, i.e. either a [String] or an
/// [InternetAddress].
- final Object bindAddress;
+ final Object? bindAddress;
/// Integer for a port to run the preview server on. If null or zero, allow
/// [HttpServer.bind] to pick one.
- final int preferredPort;
+ final int? preferredPort;
final Logger _logger;
@@ -61,12 +61,12 @@
}
/// Return the port this server is bound to.
- Future<String> get boundHostname async {
- return (await _serverFuture)?.address?.host;
+ Future<String?> get boundHostname async {
+ return (await _serverFuture)?.address.host;
}
/// Return the port this server is bound to.
- Future<int> get boundPort async {
+ Future<int?> get boundPort async {
return (await _serverFuture)?.port;
}
@@ -80,14 +80,14 @@
}
/// Begin serving HTTP requests over the given port.
- Future<int> serveHttp() async {
+ Future<int?> serveHttp() async {
if (_serverFuture != null) {
return boundPort;
}
try {
_serverFuture = HttpServer.bind(bindAddress, preferredPort ?? 0);
- var server = await _serverFuture;
+ var server = await _serverFuture!;
_handleServer(server);
return server.port;
} catch (ignore) {
diff --git a/pkg/nnbd_migration/lib/src/preview/index_file_page.dart b/pkg/nnbd_migration/lib/src/preview/index_file_page.dart
index b67d12c..3e658e1 100644
--- a/pkg/nnbd_migration/lib/src/preview/index_file_page.dart
+++ b/pkg/nnbd_migration/lib/src/preview/index_file_page.dart
@@ -10,7 +10,7 @@
class IndexFilePage extends PreviewPage {
/// Initialize a newly created index file page within the given [site].
IndexFilePage(PreviewSite site)
- : super(site, site.migrationInfo.includedRoot);
+ : super(site, site.migrationInfo!.includedRoot);
@override
bool get requiresAuth => true;
@@ -23,7 +23,7 @@
@override
Future<void> generatePage(Map<String, String> params) async {
var renderer = InstrumentationRenderer(site.migrationInfo, site.pathMapper,
- site.migrationState.hasBeenApplied, site.migrationState.needsRerun);
+ site.migrationState!.hasBeenApplied, site.migrationState!.needsRerun);
buf.write(renderer.render());
}
}
diff --git a/pkg/nnbd_migration/lib/src/preview/navigation_tree_page.dart b/pkg/nnbd_migration/lib/src/preview/navigation_tree_page.dart
index 5ee145f..2ae2183 100644
--- a/pkg/nnbd_migration/lib/src/preview/navigation_tree_page.dart
+++ b/pkg/nnbd_migration/lib/src/preview/navigation_tree_page.dart
@@ -13,7 +13,7 @@
class NavigationTreePage extends PreviewPage {
/// Initialize a newly created navigation tree page within the given [site].
NavigationTreePage(PreviewSite site)
- : super(site, site.migrationInfo.includedRoot);
+ : super(site, site.migrationInfo!.includedRoot);
@override
bool get requiresAuth => true;
diff --git a/pkg/nnbd_migration/lib/src/preview/pages.dart b/pkg/nnbd_migration/lib/src/preview/pages.dart
index 5015846..6a78297 100644
--- a/pkg/nnbd_migration/lib/src/preview/pages.dart
+++ b/pkg/nnbd_migration/lib/src/preview/pages.dart
@@ -9,7 +9,7 @@
abstract class Page {
final StringBuffer buf = StringBuffer();
- final String id;
+ final String? id;
Page(this.id);
@@ -99,7 +99,7 @@
HttpRequest request, {
int code = HttpStatus.ok,
}) async {
- if (request.headers.contentType.subType == 'json') {
+ if (request.headers.contentType!.subType == 'json') {
return respondJson(request, {'success': true}, code);
}
diff --git a/pkg/nnbd_migration/lib/src/preview/preview_page.dart b/pkg/nnbd_migration/lib/src/preview/preview_page.dart
index afc2aa1..e7b4479 100644
--- a/pkg/nnbd_migration/lib/src/preview/preview_page.dart
+++ b/pkg/nnbd_migration/lib/src/preview/preview_page.dart
@@ -12,7 +12,7 @@
/// Initialize a newly created page within the given [site]. The [id] is the
/// portion of the path to the page that follows the initial slash ('/').
- PreviewPage(this.site, String id) : super(id);
+ PreviewPage(this.site, String? id) : super(id);
/// Whether pages of this type require authorization.
bool get requiresAuth;
diff --git a/pkg/nnbd_migration/lib/src/preview/preview_site.dart b/pkg/nnbd_migration/lib/src/preview/preview_site.dart
index d648ad7..5748c5b 100644
--- a/pkg/nnbd_migration/lib/src/preview/preview_site.dart
+++ b/pkg/nnbd_migration/lib/src/preview/preview_site.dart
@@ -56,28 +56,28 @@
/// * already opted out files will remain unchanged.
class IncrementalPlan {
static final _nonWhitespaceChar = RegExp(r'\S');
- final MigrationInfo migrationInfo;
- final Map<String, UnitInfo> unitInfoMap;
- final PathMapper pathMapper;
+ final MigrationInfo? migrationInfo;
+ final Map<String?, UnitInfo> unitInfoMap;
+ final PathMapper? pathMapper;
final List<SourceFileEdit> edits;
- final Logger logger;
+ final Logger? logger;
/// The set of units which are to be opted out in this migration.
- final Set<String> optOutUnitPaths;
+ final Set<String?> optOutUnitPaths;
/// Creates a new [IncrementalPlan], extracting all of the paths which are
/// "opting out" from [navigationTree].
factory IncrementalPlan(
- MigrationInfo migrationInfo,
- Map<String, UnitInfo> unitInfoMap,
- PathMapper pathMapper,
+ MigrationInfo? migrationInfo,
+ Map<String?, UnitInfo> unitInfoMap,
+ PathMapper? pathMapper,
List<SourceFileEdit> edits,
Iterable<NavigationTreeNode> navigationTree,
- Logger logger) {
- var optOutUnitPaths = <String>{};
+ Logger? logger) {
+ var optOutUnitPaths = <String?>{};
void addUnitsToOptOut(NavigationTreeNode entity) {
if (entity is NavigationTreeDirectoryNode) {
- for (var child in entity.subtree) {
+ for (var child in entity.subtree!) {
addUnitsToOptOut(child);
}
} else {
@@ -100,32 +100,32 @@
/// Applies this migration to disk.
void apply() {
- logger.stdout('Applying migration suggestions to disk...');
+ logger!.stdout('Applying migration suggestions to disk...');
var migratedFiles = <String>[];
for (final fileEdit in edits) {
var unit = unitInfoMap[fileEdit.file];
// Decide whether to opt out; default to `false` files not included in
// [edits], like [pubspec.yaml].
var unitIsOptOut = unit != null
- ? optOutUnitPaths.contains(migrationInfo.computeName(unit))
+ ? optOutUnitPaths.contains(migrationInfo!.computeName(unit))
: false;
if (!unitIsOptOut) {
- final file = pathMapper.provider.getFile(fileEdit.file);
+ final file = pathMapper!.provider.getFile(fileEdit.file);
var code = file.exists ? file.readAsStringSync() : '';
code = SourceEdit.applySequence(code, fileEdit.edits);
file.writeAsStringSync(code);
- migratedFiles.add(migrationInfo.relativePathFromRoot(fileEdit.file));
+ migratedFiles.add(migrationInfo!.relativePathFromRoot(fileEdit.file));
}
}
// A file which is to be opted out may not be found in [edits], if all types
// were to be made non-nullable, etc. Iterate over [optOutUnitPaths] instead
// of [edits] to opt files out.
- var newlyOptedOutFiles = <String>[];
- var keptOptedOutFiles = <String>[];
+ var newlyOptedOutFiles = <String?>[];
+ var keptOptedOutFiles = <String?>[];
for (var optOutUnitPath in optOutUnitPaths) {
- var absolutePath = migrationInfo.absolutePathFromRoot(optOutUnitPath);
- var unit = unitInfoMap[absolutePath];
+ var absolutePath = migrationInfo!.absolutePathFromRoot(optOutUnitPath);
+ var unit = unitInfoMap[absolutePath]!;
if (unit.wasExplicitlyOptedOut) {
// This unit was explicitly opted out of null safety with a Dart
// Language version comment. Leave the unit be.
@@ -134,7 +134,7 @@
// This unit was not yet migrated at the start, was not explicitly
// opted out at the start, and is being opted out now. Add a Dart
// Language version comment.
- final file = pathMapper.provider.getFile(absolutePath);
+ final file = pathMapper!.provider.getFile(absolutePath);
var code = file.exists ? file.readAsStringSync() : '';
file.writeAsStringSync(optCodeOutOfNullSafety(code));
newlyOptedOutFiles.add(optOutUnitPath);
@@ -152,19 +152,19 @@
}
void _logFileStatus(
- List<String> files, String Function(String text) template) {
+ List<String?> files, String Function(String text) template) {
if (files.isNotEmpty) {
var count = files.length;
if (count <= 20) {
var s = count > 1 ? 's' : '';
var text = '$count file$s';
- logger.stdout('${template(text)}:');
+ logger!.stdout('${template(text)}:');
for (var path in files) {
- logger.stdout(' $path');
+ logger!.stdout(' $path');
}
} else {
var text = '$count files';
- logger.stdout('${template(text)}.');
+ logger!.stdout('${template(text)}.');
}
}
}
@@ -299,20 +299,20 @@
static const rerunMigrationPath = '/rerun-migration';
/// The state of the migration being previewed.
- MigrationState migrationState;
+ MigrationState? migrationState;
/// A table mapping the paths of files to the information about the
/// compilation units at those paths.
- final Map<String, UnitInfo> unitInfoMap = {};
+ final Map<String?, UnitInfo> unitInfoMap = {};
// A function provided by DartFix to rerun the migration.
- final Future<MigrationState> Function() rerunFunction;
+ final Future<MigrationState?> Function() rerunFunction;
/// Callback function that should be invoked after successfully applying
/// migration.
final void Function() applyHook;
- final Logger logger;
+ final Logger? logger;
final String serviceAuthToken = _makeAuthToken();
@@ -326,11 +326,11 @@
/// Return the information about the migration that will be used to serve up
/// pages.
- MigrationInfo get migrationInfo => migrationState.migrationInfo;
+ MigrationInfo? get migrationInfo => migrationState!.migrationInfo;
/// Return the path mapper used to map paths from the unit infos to the paths
/// being served.
- PathMapper get pathMapper => migrationState.pathMapper;
+ PathMapper? get pathMapper => migrationState!.pathMapper;
@override
Page createExceptionPage(String message, StackTrace trace) {
@@ -369,7 +369,7 @@
Future<void> handleGetRequest(HttpRequest request) async {
var uri = request.uri;
var path = uri.path;
- var decodedPath = pathMapper.reverseMap(uri);
+ var decodedPath = pathMapper!.reverseMap(uri);
try {
if (path == highlightCssPath) {
// Note: `return await` needed due to
@@ -400,9 +400,9 @@
// https://github.com/dart-lang/sdk/issues/39204
return await respond(request, RobotoMonoPage(this));
} else if (path == '/' ||
- decodedPath == migrationInfo.includedRoot ||
+ decodedPath == migrationInfo!.includedRoot ||
decodedPath ==
- '${migrationInfo.includedRoot}${pathMapper.separator}') {
+ '${migrationInfo!.includedRoot}${pathMapper!.separator}') {
// Note: `return await` needed due to
// https://github.com/dart-lang/sdk/issues/39204
return await respond(request, IndexFilePage(this));
@@ -453,12 +453,12 @@
} else if (path == rerunMigrationPath) {
await rerunMigration();
- if (migrationState.hasErrors) {
+ if (migrationState!.hasErrors) {
return await respondJson(
request,
{
'success': false,
- 'errors': migrationState.analysisResult.toJson(),
+ 'errors': migrationState!.analysisResult!.toJson(),
},
HttpStatus.ok);
} else {
@@ -483,17 +483,17 @@
/// Perform the migration.
void performApply(Iterable<NavigationTreeNode> navigationTree) {
- if (migrationState.hasBeenApplied) {
+ if (migrationState!.hasBeenApplied) {
throw StateError(
'It looks like this migration has already been applied. Try'
' restarting the migration tool if this is not the case.');
}
- final edits = migrationState.listener.sourceChange.edits;
+ final edits = migrationState!.listener!.sourceChange.edits;
// Perform a full check that no files have changed before touching the disk.
for (final fileEdit in edits) {
- final file = pathMapper.provider.getFile(fileEdit.file);
+ final file = pathMapper!.provider.getFile(fileEdit.file);
if (!file.path.endsWith('.dart')) {
continue;
}
@@ -514,7 +514,7 @@
}
// Eagerly mark the migration applied. If this throws, we cannot go back.
- migrationState.markApplied();
+ migrationState!.markApplied();
IncrementalPlan(migrationInfo, unitInfoMap, pathMapper, edits,
navigationTree, logger)
.apply();
@@ -527,18 +527,18 @@
// Update the code on disk.
//
var params = uri.queryParameters;
- var path = pathMapper.reverseMap(uri);
- var offset = int.parse(params['offset']);
- var end = int.parse(params['end']);
- var replacement = params['replacement'];
- var file = pathMapper.provider.getFile(path);
+ var path = pathMapper!.reverseMap(uri);
+ var offset = int.parse(params['offset']!);
+ var end = int.parse(params['end']!);
+ var replacement = params['replacement']!;
+ var file = pathMapper!.provider.getFile(path);
var diskContent = file.readAsStringSync();
- if (!unitInfoMap[path].hadDiskContent(diskContent)) {
+ if (!unitInfoMap[path]!.hadDiskContent(diskContent)) {
throw StateError('Cannot perform edit. This file has been changed since'
' last migration run. Press the "rerun from sources" button and then'
' try again. (Changed file path is ${file.path})');
}
- final unitInfo = unitInfoMap[path];
+ final unitInfo = unitInfoMap[path]!;
final diskMapper = unitInfo.diskChangesOffsetMapper;
final diskOffsetStart = diskMapper.map(offset);
final diskOffsetEnd = diskMapper.map(end);
@@ -547,7 +547,7 @@
' a previous hint action. Rerun the migration and try again.');
}
unitInfo.handleSourceEdit(SourceEdit(offset, end - offset, replacement));
- migrationState.needsRerun = true;
+ migrationState!.needsRerun = true;
var newContent =
diskContent.replaceRange(diskOffsetStart, diskOffsetEnd, replacement);
file.writeAsStringSync(newContent);
@@ -556,7 +556,7 @@
/// Perform the hint edit indicated by the [hintAction].
Future<void> performHintAction(HintAction hintAction) async {
- final node = migrationState.nodeMapper.nodeForId(hintAction.nodeId);
+ final node = migrationState!.nodeMapper.nodeForId(hintAction.nodeId)!;
final edits = node.hintActions[hintAction.kind];
if (edits == null) {
throw StateError('This edit was not available to perform.');
@@ -564,20 +564,20 @@
//
// Update the code on disk.
//
- var path = node.codeReference.path;
- var file = pathMapper.provider.getFile(path);
+ var path = node.codeReference!.path;
+ var file = pathMapper!.provider.getFile(path);
var diskContent = file.readAsStringSync();
- if (!unitInfoMap[path].hadDiskContent(diskContent)) {
+ if (!unitInfoMap[path]!.hadDiskContent(diskContent)) {
throw StateError('Cannot perform edit. This file has been changed since'
' last migration run. Press the "rerun from sources" button and then'
' try again. (Changed file path is ${file.path})');
}
- final unitInfo = unitInfoMap[path];
+ final unitInfo = unitInfoMap[path]!;
final diskMapper = unitInfo.diskChangesOffsetMapper;
var newContent = diskContent;
- migrationState.needsRerun = true;
+ migrationState!.needsRerun = true;
for (final entry in edits.entries) {
- final offset = entry.key;
+ final offset = entry.key!;
final edits = entry.value;
final diskOffset = diskMapper.map(offset);
if (diskOffset == null) {
@@ -586,7 +586,7 @@
' a previous hint action. Rerun the migration and try again.');
}
final unmappedSourceEdit = edits.toSourceEdit(offset);
- final diskSourceEdit = edits.toSourceEdit(diskMapper.map(offset));
+ final diskSourceEdit = edits.toSourceEdit(diskMapper.map(offset)!);
unitInfo.handleSourceEdit(unmappedSourceEdit);
newContent = diskSourceEdit.apply(newContent);
}
@@ -594,32 +594,32 @@
unitInfo.diskContent = newContent;
}
- Future<Map<String, Object>> requestBodyJson(HttpRequest request) async =>
+ Future<Map<String, Object?>> requestBodyJson(HttpRequest request) async =>
(await request
.map((entry) => entry.map((i) => i.toInt()).toList())
.transform<String>(Utf8Decoder())
.transform(JsonDecoder())
- .single) as Map<String, Object>;
+ .single) as Map<String, Object?>;
Future<void> rerunMigration() async {
migrationState = await rerunFunction();
- if (!migrationState.hasErrors) {
+ if (!migrationState!.hasErrors) {
reset();
}
}
void reset() {
unitInfoMap.clear();
- var unitInfos = migrationInfo.units;
- var provider = pathMapper.provider;
+ var unitInfos = migrationInfo!.units!;
+ var provider = pathMapper!.provider;
for (var unit in unitInfos) {
unitInfoMap[unit.path] = unit;
}
- for (var unit in migrationInfo.unitMap.values) {
+ for (var unit in migrationInfo!.unitMap.values) {
if (!unitInfos.contains(unit)) {
if (unit.content == null) {
try {
- unit.content = provider.getFile(unit.path).readAsStringSync();
+ unit.content = provider.getFile(unit.path!).readAsStringSync();
} catch (_) {
// If we can't read the content of the file, then skip it.
continue;
@@ -668,7 +668,7 @@
Future<void> _respondInternalError(HttpRequest request, String path,
dynamic exception, StackTrace stackTrace) async {
try {
- if (request.headers.contentType.subType == 'json') {
+ if (request.headers.contentType!.subType == 'json') {
return await respondJson(
request,
{
diff --git a/pkg/nnbd_migration/lib/src/preview/region_page.dart b/pkg/nnbd_migration/lib/src/preview/region_page.dart
index 8b3cbab..2a6ac8f 100644
--- a/pkg/nnbd_migration/lib/src/preview/region_page.dart
+++ b/pkg/nnbd_migration/lib/src/preview/region_page.dart
@@ -17,7 +17,7 @@
/// Initialize a newly created region page within the given [site]. The
/// [unitInfo] provides the information needed to render the page.
RegionPage(PreviewSite site, this.unitInfo)
- : super(site, unitInfo.path.substring(1));
+ : super(site, unitInfo.path!.substring(1));
@override
bool get requiresAuth => true;
@@ -29,7 +29,7 @@
@override
Future<void> generatePage(Map<String, String> params) async {
- var region = unitInfo.regionAt(int.parse(params['offset']));
+ var region = unitInfo.regionAt(int.parse(params['offset']!));
var renderer = RegionRenderer(region, unitInfo, site.migrationInfo,
site.pathMapper, site.serviceAuthToken);
buf.write(jsonEncode(renderer.render().toJson()));
diff --git a/pkg/nnbd_migration/lib/src/utilities/completeness_tracker.dart b/pkg/nnbd_migration/lib/src/utilities/completeness_tracker.dart
index 199eb9a..520fda8 100644
--- a/pkg/nnbd_migration/lib/src/utilities/completeness_tracker.dart
+++ b/pkg/nnbd_migration/lib/src/utilities/completeness_tracker.dart
@@ -14,32 +14,32 @@
/// Mixing in this class should have very low overhead when assertions are
/// disabled.
mixin CompletenessTracker<T> on AstVisitor<T>, PermissiveModeVisitor<T> {
- AnnotationTracker _annotationTracker;
- TypeNameTracker _typeNameTracker;
+ AnnotationTracker? _annotationTracker;
+ TypeNameTracker? _typeNameTracker;
@override
- T visitAnnotation(Annotation node) {
+ T? visitAnnotation(Annotation node) {
annotationVisited(node);
return super.visitAnnotation(node);
}
void annotationVisited(Annotation node) {
assert(() {
- _annotationTracker.nodeVisited(node);
+ _annotationTracker!.nodeVisited(node);
return true;
}());
}
void typeNameVisited(TypeName node) {
assert(() {
- _typeNameTracker.nodeVisited(node);
+ _typeNameTracker!.nodeVisited(node);
return true;
}());
}
@override
- T visitCompilationUnit(CompilationUnit node) {
- T result;
+ T? visitCompilationUnit(CompilationUnit node) {
+ T? result;
reportExceptionsIfPermissive(node, () {
assert(() {
assert(_annotationTracker == null);
@@ -51,8 +51,8 @@
try {
result = super.visitCompilationUnit(node);
assert(() {
- _annotationTracker.finalize();
- _typeNameTracker.finalize();
+ _annotationTracker!.finalize();
+ _typeNameTracker!.finalize();
return true;
}());
} finally {
diff --git a/pkg/nnbd_migration/lib/src/utilities/hint_utils.dart b/pkg/nnbd_migration/lib/src/utilities/hint_utils.dart
index 9f9a8c7..79d4edd 100644
--- a/pkg/nnbd_migration/lib/src/utilities/hint_utils.dart
+++ b/pkg/nnbd_migration/lib/src/utilities/hint_utils.dart
@@ -8,8 +8,8 @@
/// Determines if the given [token] is followed by a nullability hint, and if
/// so, returns information about it. Otherwise returns `null`.
-HintComment getPostfixHint(Token token) {
- var commentToken = token.next.precedingComments;
+HintComment? getPostfixHint(Token token) {
+ var commentToken = token.next!.precedingComments;
if (commentToken != null) {
HintCommentKind kind;
if (commentToken.lexeme == '/*!*/') {
@@ -33,11 +33,11 @@
/// Determines if the given [token] is preceded by a hint, and if so, returns
/// information about it. Otherwise returns `null`.
-HintComment getPrefixHint(Token token) {
- Token commentToken = token.precedingComments;
+HintComment? getPrefixHint(Token token) {
+ Token? commentToken = token.precedingComments;
if (commentToken != null) {
while (true) {
- var nextComment = commentToken.next;
+ var nextComment = commentToken!.next;
if (nextComment == null) break;
commentToken = nextComment;
}
@@ -124,14 +124,14 @@
/// Creates the changes necessary to accept the given hint (replace it with
/// its contents and fix up whitespace).
- Map<int, List<AtomicEdit>> changesToAccept(String sourceText,
- {AtomicEditInfo info}) {
+ Map<int?, List<AtomicEdit>> changesToAccept(String? sourceText,
+ {AtomicEditInfo? info}) {
bool prependSpace = false;
bool appendSpace = false;
var removeOffset = _removeOffset;
var removeEnd = _removeEnd;
if (_isIdentifierCharBeforeOffset(sourceText, removeOffset) &&
- _isIdentifierCharAtOffset(sourceText, _keepOffset)) {
+ _isIdentifierCharAtOffset(sourceText!, _keepOffset)) {
if (sourceText[removeOffset] == ' ') {
// We can just keep this space.
removeOffset++;
@@ -140,7 +140,7 @@
}
}
if (_isIdentifierCharBeforeOffset(sourceText, _keepEnd) &&
- _isIdentifierCharAtOffset(sourceText, removeEnd)) {
+ _isIdentifierCharAtOffset(sourceText!, removeEnd)) {
if (sourceText[removeEnd - 1] == ' ') {
// We can just keep this space.
removeEnd--;
@@ -161,12 +161,12 @@
/// Creates the changes necessary to remove the given hint (and fix up
/// whitespace).
- Map<int, List<AtomicEdit>> changesToRemove(String sourceText,
- {AtomicEditInfo info}) {
+ Map<int?, List<AtomicEdit>> changesToRemove(String? sourceText,
+ {AtomicEditInfo? info}) {
bool appendSpace = false;
var removeOffset = _removeOffset;
if (_isIdentifierCharBeforeOffset(sourceText, removeOffset) &&
- _isIdentifierCharAtOffset(sourceText, _removeEnd)) {
+ _isIdentifierCharAtOffset(sourceText!, _removeEnd)) {
if (sourceText[removeOffset] == ' ') {
// We can just keep this space.
removeOffset++;
@@ -184,9 +184,9 @@
/// Creates the changes necessary to replace the given hint with a different
/// hint.
- Map<int, List<AtomicEdit>> changesToReplace(
- String sourceText, String replacement,
- {AtomicEditInfo info}) {
+ Map<int?, List<AtomicEdit>> changesToReplace(
+ String? sourceText, String replacement,
+ {AtomicEditInfo? info}) {
return {
_commentOffset: [
AtomicEdit.replace(_commentEnd - _commentOffset, replacement,
@@ -200,8 +200,9 @@
_identifierCharRegexp.hasMatch(sourceText[offset]);
}
- static bool _isIdentifierCharBeforeOffset(String sourceText, int offset) {
- return offset > 0 && _identifierCharRegexp.hasMatch(sourceText[offset - 1]);
+ static bool _isIdentifierCharBeforeOffset(String? sourceText, int offset) {
+ return offset > 0 &&
+ _identifierCharRegexp.hasMatch(sourceText![offset - 1]);
}
}
@@ -230,7 +231,7 @@
extension FormalParameterExtensions on FormalParameter {
// TODO(srawlins): Add this to FormalParameter interface.
- Token get firstTokenAfterCommentAndMetadata {
+ Token? get firstTokenAfterCommentAndMetadata {
var parameter = this is DefaultFormalParameter
? (this as DefaultFormalParameter).parameter
: this as NormalFormalParameter;
@@ -238,7 +239,7 @@
if (parameter.keyword != null) {
return parameter.keyword;
} else if (parameter.type != null) {
- return parameter.type.beginToken;
+ return parameter.type!.beginToken;
} else {
return parameter.thisKeyword;
}
@@ -246,7 +247,7 @@
if (parameter.covariantKeyword != null) {
return parameter.covariantKeyword;
} else if (parameter.returnType != null) {
- return parameter.returnType.beginToken;
+ return parameter.returnType!.beginToken;
} else {
return parameter.identifier.token;
}
@@ -256,9 +257,9 @@
} else if (parameter.keyword != null) {
return parameter.keyword;
} else if (parameter.type != null) {
- return parameter.type.beginToken;
+ return parameter.type!.beginToken;
} else {
- return parameter.identifier.token;
+ return parameter.identifier!.token;
}
}
return null;
diff --git a/pkg/nnbd_migration/lib/src/utilities/json.dart b/pkg/nnbd_migration/lib/src/utilities/json.dart
index 36fd728..e93013b 100644
--- a/pkg/nnbd_migration/lib/src/utilities/json.dart
+++ b/pkg/nnbd_migration/lib/src/utilities/json.dart
@@ -6,7 +6,7 @@
///
/// If [map] has key [key], return the value paired with [key]; otherwise throw
/// a FormatException.
-dynamic expectKey(Map<Object, Object> map, String key) {
+dynamic expectKey(Map<Object?, Object?> map, String key) {
if (map.containsKey(key)) {
return map[key];
}
@@ -18,7 +18,7 @@
///
/// If [object] is of type [T], return it; otherwise throw a FormatException
/// with [errorKey] in the message.
-T expectType<T>(Object object, String errorKey) {
+T expectType<T>(Object? object, String errorKey) {
if (object is T) {
return object;
}
diff --git a/pkg/nnbd_migration/lib/src/utilities/multi_future_tracker.dart b/pkg/nnbd_migration/lib/src/utilities/multi_future_tracker.dart
index 0e30a59..94176c2 100644
--- a/pkg/nnbd_migration/lib/src/utilities/multi_future_tracker.dart
+++ b/pkg/nnbd_migration/lib/src/utilities/multi_future_tracker.dart
@@ -63,7 +63,7 @@
/// Generates a [Future] from the given closure and adds it to the queue,
/// once the queue is sufficiently empty. Completes when the generated
/// closure completes.
- Future<T> runFutureFromClosure<T>(FutureOr<T> Function() closure) async {
+ Future<T> runFutureFromClosure<T>(FutureOr<T>? Function() closure) async {
Completer<T> futureComplete = Completer();
await addFutureFromClosure(() async {
futureComplete.complete(await closure());
diff --git a/pkg/nnbd_migration/lib/src/utilities/permissive_mode.dart b/pkg/nnbd_migration/lib/src/utilities/permissive_mode.dart
index 1d19c78..dd64a77 100644
--- a/pkg/nnbd_migration/lib/src/utilities/permissive_mode.dart
+++ b/pkg/nnbd_migration/lib/src/utilities/permissive_mode.dart
@@ -13,10 +13,10 @@
///
/// If the [listener] is `null`, exceptions are not caught.
mixin PermissiveModeVisitor<T> on GeneralizingAstVisitor<T> {
- NullabilityMigrationListener /*?*/ get listener;
+ NullabilityMigrationListener? get listener;
/// The file being analyzed.
- Source get source;
+ Source? get source;
/// Executes [callback]. If [listener] is not `null`, and an exception
/// occurs, the exception is caught and reported to the [listener].
@@ -25,7 +25,7 @@
try {
return callback();
} catch (exception, stackTrace) {
- listener.reportException(source, node, exception, stackTrace);
+ listener!.reportException(source, node, exception, stackTrace);
}
} else {
callback();
@@ -33,12 +33,12 @@
}
@override
- T visitNode(AstNode node) {
+ T? visitNode(AstNode node) {
if (listener != null) {
try {
return super.visitNode(node);
} catch (exception, stackTrace) {
- listener.reportException(source, node, exception, stackTrace);
+ listener!.reportException(source, node, exception, stackTrace);
return null;
}
} else {
diff --git a/pkg/nnbd_migration/lib/src/utilities/progress_bar.dart b/pkg/nnbd_migration/lib/src/utilities/progress_bar.dart
index e1b7931..60a57ca 100644
--- a/pkg/nnbd_migration/lib/src/utilities/progress_bar.dart
+++ b/pkg/nnbd_migration/lib/src/utilities/progress_bar.dart
@@ -21,17 +21,17 @@
/// If there is no terminal, the progress bar will not be drawn.
class ProgressBar {
/// Whether the progress bar should be drawn.
- /*late*/ bool _shouldDrawProgress;
+ late bool _shouldDrawProgress;
/// The width of the terminal, in terms of characters.
- /*late*/ int _width;
+ late int _width;
final Logger _logger;
/// The inner width of the terminal, in terms of characters.
///
/// This represents the number of characters available for drawing progress.
- /*late*/ int _innerWidth;
+ late int _innerWidth;
final int _totalTickCount;
diff --git a/pkg/nnbd_migration/lib/src/utilities/resolution_utils.dart b/pkg/nnbd_migration/lib/src/utilities/resolution_utils.dart
index 81624db..3971a35 100644
--- a/pkg/nnbd_migration/lib/src/utilities/resolution_utils.dart
+++ b/pkg/nnbd_migration/lib/src/utilities/resolution_utils.dart
@@ -21,7 +21,7 @@
/// This mixin provides utilities that are useful to visitors implementing
/// resolution-like behaviors.
mixin ResolutionUtils {
- List<String> _objectGetNames;
+ List<String>? _objectGetNames;
TypeProvider get typeProvider;
diff --git a/pkg/nnbd_migration/lib/src/utilities/scoped_set.dart b/pkg/nnbd_migration/lib/src/utilities/scoped_set.dart
index ea62206..702e17b 100644
--- a/pkg/nnbd_migration/lib/src/utilities/scoped_set.dart
+++ b/pkg/nnbd_migration/lib/src/utilities/scoped_set.dart
@@ -38,7 +38,7 @@
void doScoped(
{List<T> elements = const [],
bool copyCurrent = false,
- void Function() action}) {
+ required void Function() action}) {
pushScope(elements: elements, copyCurrent: copyCurrent);
try {
action();
diff --git a/pkg/nnbd_migration/lib/src/utilities/source_edit_diff_formatter.dart b/pkg/nnbd_migration/lib/src/utilities/source_edit_diff_formatter.dart
index b0908c1..5ad8018 100644
--- a/pkg/nnbd_migration/lib/src/utilities/source_edit_diff_formatter.dart
+++ b/pkg/nnbd_migration/lib/src/utilities/source_edit_diff_formatter.dart
@@ -55,7 +55,7 @@
}
prevOffset = offset;
}
- for (var edit in edits[offset]) {
+ for (var edit in edits[offset]!) {
if (edit.length > 0) {
var offset = prevOffset + edit.length;
var text = origText.substring(prevOffset, offset);
@@ -89,7 +89,7 @@
String inserted(String text);
- String lineHeader(int lineNum, String separator) {
+ String lineHeader(int? lineNum, String separator) {
const String emptyLineHeader = ' ';
if (lineNum == null) {
return emptyLineHeader + separator;
diff --git a/pkg/nnbd_migration/lib/src/utilities/subprocess_launcher.dart b/pkg/nnbd_migration/lib/src/utilities/subprocess_launcher.dart
index 814525f..672840e 100644
--- a/pkg/nnbd_migration/lib/src/utilities/subprocess_launcher.dart
+++ b/pkg/nnbd_migration/lib/src/utilities/subprocess_launcher.dart
@@ -36,37 +36,34 @@
/// From flutter:dev/tools/dartdoc.dart, modified.
static Future<void> _printStream(Stream<List<int>> stream, Stdout output,
- {String prefix = '', Iterable<String> Function(String line) filter}) {
- assert(prefix != null);
+ {String prefix = '', Iterable<String> Function(String line)? filter}) {
filter ??= (line) => [line];
return stream
.transform(utf8.decoder)
.transform(const LineSplitter())
.expand(filter)
.listen((String line) {
- if (line != null) {
- output.write('$prefix$line'.trim());
- output.write('\n');
- }
+ output.write('$prefix$line'.trim());
+ output.write('\n');
}).asFuture();
}
- SubprocessLauncher(this.context, [Map<String, String> environment])
+ SubprocessLauncher(this.context, [Map<String, String>? environment])
: environmentDefaults = environment ?? <String, String>{};
/// Wraps [runStreamedImmediate] as a closure around
/// [maxParallel.addFutureFromClosure].
///
/// This essentially implements a 'make -j N' limit for all subcommands.
- Future<Iterable<Map>> runStreamed(String executable, List<String> arguments,
+ Future<Iterable<Map>?> runStreamed(String executable, List<String> arguments,
// TODO(jcollins-g): Fix primitive obsession: consolidate parameters into
// another object.
- {String workingDirectory,
- Map<String, String> environment,
+ {String? workingDirectory,
+ Map<String, String>? environment,
bool includeParentEnvironment = true,
- void Function(String) perLine,
+ void Function(String)? perLine,
int retries = 0,
- String instance,
+ String? instance,
bool allowNonzeroExit = false}) async {
// TODO(jcollins-g): The closure wrapping we've done has made it impossible
// to catch exceptions when calling runStreamed. Fix this.
@@ -93,17 +90,17 @@
/// Windows (though some of the bashisms will no longer make sense).
/// TODO(jcollins-g): refactor to return a stream of stderr/stdout lines
/// and their associated JSON objects.
- Future<Iterable<Map>> runStreamedImmediate(
+ Future<Iterable<Map>?> runStreamedImmediate(
String executable, List<String> arguments,
- {String workingDirectory,
- Map<String, String> environment,
+ {String? workingDirectory,
+ Map<String, String>? environment,
bool includeParentEnvironment = true,
- void Function(String) perLine,
+ void Function(String)? perLine,
// A tag added to [context] to construct the line prefix.
// Use this to indicate the process or processes with the tag
// share something in common, like a hostname, a package, or a
// multi-step procedure.
- String instance,
+ String? instance,
bool allowNonzeroExit = false}) async {
String prefix = context.isNotEmpty
? '$context${instance != null ? "-$instance" : ""}: '
@@ -111,22 +108,22 @@
environment ??= {};
environment.addAll(environmentDefaults);
- List<Map> jsonObjects;
+ List<Map>? jsonObjects;
/// Parses json objects generated by the subprocess. If a json object
/// contains the key 'message' or the keys 'data' and 'text', return that
/// value as a collection of lines suitable for printing.
Iterable<String> jsonCallback(String line) {
if (perLine != null) perLine(line);
- Map result;
+ Map? result;
try {
- result = json.decoder.convert(line) as Map;
+ result = json.decoder.convert(line) as Map?;
} on FormatException {
// ignore
}
if (result != null) {
jsonObjects ??= [];
- jsonObjects.add(result);
+ jsonObjects!.add(result);
if (result.containsKey('message')) {
line = result['message'] as String;
} else if (result.containsKey('data') &&
@@ -140,16 +137,14 @@
stderr.write('$prefix+ ');
if (workingDirectory != null) stderr.write('(cd "$workingDirectory" && ');
- if (environment != null) {
- stderr.write(environment.keys.map((String key) {
- if (environment[key].contains(quotables)) {
- return "$key='${environment[key]}'";
- } else {
- return '$key=${environment[key]}';
- }
- }).join(' '));
- stderr.write(' ');
- }
+ stderr.write(environment.keys.map((String key) {
+ if (environment![key]!.contains(quotables)) {
+ return "$key='${environment[key]}'";
+ } else {
+ return '$key=${environment[key]}';
+ }
+ }).join(' '));
+ stderr.write(' ');
stderr.write('$executable');
if (arguments.isNotEmpty) {
for (String arg in arguments) {
diff --git a/pkg/nnbd_migration/lib/src/utilities/where_or_null_transformer.dart b/pkg/nnbd_migration/lib/src/utilities/where_or_null_transformer.dart
index 067ee7d..d276ad5 100644
--- a/pkg/nnbd_migration/lib/src/utilities/where_or_null_transformer.dart
+++ b/pkg/nnbd_migration/lib/src/utilities/where_or_null_transformer.dart
@@ -64,8 +64,8 @@
/// If [expression] is the expression part of the `orElse` argument of a call
/// that can be transformed, returns information about the transformable call;
/// otherwise returns `null`.
- WhereOrNullTransformationInfo tryTransformOrElseArgument(
- Expression expression) {
+ WhereOrNullTransformationInfo? tryTransformOrElseArgument(
+ Expression? expression) {
var transformationInfo =
_tryTransformMethodInvocation(expression?.parent?.parent?.parent);
if (transformationInfo != null &&
@@ -79,8 +79,8 @@
/// Searches [argumentList] for a named argument with the name "orElse". If
/// such an argument is found, and no other named arguments are found, it is
/// returned; otherwise `null` is returned.
- NamedExpression _findOrElseArgument(ArgumentList argumentList) {
- NamedExpression orElseArgument;
+ NamedExpression? _findOrElseArgument(ArgumentList argumentList) {
+ NamedExpression? orElseArgument;
for (var argument in argumentList.arguments) {
if (argument is NamedExpression) {
if (argument.name.label.name == 'orElse') {
@@ -99,7 +99,7 @@
/// Determines if [element] is a method that can be transformed; if it can,
/// the name of the replacement is returned; otherwise, `null` is returned.
- String _getTransformableMethodReplacementName(Element element) {
+ String? _getTransformableMethodReplacementName(Element? element) {
if (element is MethodElement) {
if (element.isStatic) return null;
var replacementName = _replacementNames[element.name];
@@ -121,7 +121,7 @@
bool _isClosureReturningNull(Expression expression) {
if (expression is FunctionExpression) {
if (expression.typeParameters != null) return false;
- if (expression.parameters.parameters.isNotEmpty) return false;
+ if (expression.parameters!.parameters.isNotEmpty) return false;
var body = expression.body;
if (body is ExpressionFunctionBody) {
if (body.expression is NullLiteral) return true;
@@ -132,7 +132,7 @@
/// If [node] is a call that can be transformed, returns information about the
/// transformable call; otherwise returns `null`.
- WhereOrNullTransformationInfo _tryTransformMethodInvocation(AstNode node) {
+ WhereOrNullTransformationInfo? _tryTransformMethodInvocation(AstNode? node) {
if (node is MethodInvocation) {
var replacementName =
_getTransformableMethodReplacementName(node.methodName.staticElement);
diff --git a/pkg/nnbd_migration/lib/src/variables.dart b/pkg/nnbd_migration/lib/src/variables.dart
index d873271..44d0475 100644
--- a/pkg/nnbd_migration/lib/src/variables.dart
+++ b/pkg/nnbd_migration/lib/src/variables.dart
@@ -44,30 +44,30 @@
final TypeProvider _typeProvider;
- final _conditionalDiscards = <Source, Map<int, ConditionalDiscard>>{};
+ final _conditionalDiscards = <Source?, Map<int, ConditionalDiscard>>{};
- final _decoratedElementTypes = <Element, DecoratedType>{};
+ final _decoratedElementTypes = <Element?, DecoratedType?>{};
final _decoratedDirectSupertypes =
- <ClassElement, Map<ClassElement, DecoratedType>>{};
+ <ClassElement, Map<ClassElement, DecoratedType?>>{};
- final _decoratedTypeAnnotations = <Source, Map<int, DecoratedType>>{};
+ final _decoratedTypeAnnotations = <Source?, Map<int, DecoratedType>>{};
- final _expressionChecks = <Source, Map<int, ExpressionChecks>>{};
+ final _expressionChecks = <Source?, Map<int, ExpressionChecks>>{};
- final _lateHints = <Source, Map<int, HintComment>>{};
+ final _lateHints = <Source?, Map<int, HintComment>>{};
- final _nullCheckHints = <Source, Map<int, HintComment>>{};
+ final _nullCheckHints = <Source?, Map<int, HintComment>>{};
- final _nullabilityHints = <Source, Map<int, HintComment>>{};
+ final _nullabilityHints = <Source?, Map<int, HintComment>>{};
- final _requiredHints = <Source, Map<int, HintComment>>{};
+ final _requiredHints = <Source?, Map<int, HintComment>>{};
- final _unnecessaryCasts = <Source, Set<int>>{};
+ final _unnecessaryCasts = <Source?, Set<int>>{};
final AlreadyMigratedCodeDecorator _alreadyMigratedCodeDecorator;
- final NullabilityMigrationInstrumentation /*?*/ instrumentation;
+ final NullabilityMigrationInstrumentation? instrumentation;
final LineInfo Function(String) _getLineInfo;
@@ -78,7 +78,7 @@
/// Given a [class_], gets the decorated type information for the superclasses
/// it directly implements/extends/etc.
- Map<ClassElement, DecoratedType> decoratedDirectSupertypes(
+ Map<ClassElement, DecoratedType?> decoratedDirectSupertypes(
ClassElement class_) {
return _decoratedDirectSupertypes[class_] ??=
_decorateDirectSupertypes(class_);
@@ -99,18 +99,18 @@
/// Gets the [DecoratedType] associated with the given [typeAnnotation].
DecoratedType decoratedTypeAnnotation(
- Source source, TypeAnnotation typeAnnotation) {
+ Source? source, TypeAnnotation typeAnnotation) {
var annotationsInSource = _decoratedTypeAnnotations[source];
if (annotationsInSource == null) {
- throw StateError('No declarated type annotations in ${source.fullName}; '
+ throw StateError('No declarated type annotations in ${source!.fullName}; '
'expected one for ${typeAnnotation.toSource()} '
'(offset ${typeAnnotation.offset})');
}
- DecoratedType decoratedTypeAnnotation = annotationsInSource[
+ DecoratedType? decoratedTypeAnnotation = annotationsInSource[
uniqueIdentifierForSpan(typeAnnotation.offset, typeAnnotation.end)];
if (decoratedTypeAnnotation == null) {
throw StateError('Missing declarated type annotation'
- ' in ${source.fullName}; for ${typeAnnotation.toSource()}');
+ ' in ${source!.fullName}; for ${typeAnnotation.toSource()}');
}
return decoratedTypeAnnotation;
}
@@ -126,10 +126,11 @@
/// because at that point the types we are dealing with are all
/// post-migration types, so their bounds already reflect the correct
/// nullabilities.
- DecoratedType decoratedTypeParameterBound(TypeParameterElement typeParameter,
+ DecoratedType? decoratedTypeParameterBound(TypeParameterElement typeParameter,
{bool allowNullUnparentedBounds = false}) {
var enclosingElement = typeParameter.enclosingElement;
- var decoratedType = DecoratedTypeParameterBounds.current.get(typeParameter);
+ var decoratedType =
+ DecoratedTypeParameterBounds.current!.get(typeParameter);
if (enclosingElement == null) {
if (decoratedType == null && !allowNullUnparentedBounds) {
throw StateError(
@@ -138,7 +139,7 @@
}
} else {
if (decoratedType == null) {
- if (_graph.isBeingMigrated(typeParameter.library.source)) {
+ if (_graph.isBeingMigrated(typeParameter.library!.source)) {
throw StateError(
'A decorated type for the bound of $typeParameter should '
'have been stored by the NodeBuilder via '
@@ -151,7 +152,7 @@
target);
instrumentation?.externalDecoratedTypeParameterBound(
typeParameter, decoratedType);
- DecoratedTypeParameterBounds.current.put(typeParameter, decoratedType);
+ DecoratedTypeParameterBounds.current!.put(typeParameter, decoratedType);
}
}
return decoratedType;
@@ -159,24 +160,24 @@
/// Retrieves the [ExpressionChecks] object corresponding to the given
/// [expression], if one exists; otherwise null.
- ExpressionChecks expressionChecks(Source source, Expression expression) {
+ ExpressionChecks? expressionChecks(Source? source, Expression expression) {
return (_expressionChecks[source] ??
{})[uniqueIdentifierForSpan(expression.offset, expression.end)];
}
- ConditionalDiscard getConditionalDiscard(Source source, AstNode node) =>
+ ConditionalDiscard? getConditionalDiscard(Source? source, AstNode node) =>
(_conditionalDiscards[source] ?? {})[node.offset];
/// If the given [node] is preceded by a `/*late*/` hint, returns the
/// HintComment for it; otherwise returns `null`. See [recordLateHint].
- HintComment getLateHint(Source source, VariableDeclarationList node) {
+ HintComment? getLateHint(Source? source, VariableDeclarationList node) {
return (_lateHints[source] ?? {})[node.offset];
}
/// If the given [node] is followed by a `/*?*/` or /*!*/ hint, returns the
/// HintComment for it; otherwise returns `null`. See
/// [recordNullabilityHint].
- HintComment getNullabilityHint(Source source, AstNode node) {
+ HintComment? getNullabilityHint(Source? source, AstNode node) {
assert(node is TypeAnnotation ||
node is FunctionTypedFormalParameter ||
(node is FieldFormalParameter && node.parameters != null));
@@ -187,37 +188,37 @@
/// If the given [expression] is followed by a null check hint (`/*!*/`),
/// returns the HintComment for it; otherwise returns `null`. See
/// [recordNullCheckHint].
- HintComment getNullCheckHint(Source source, Expression expression) {
+ HintComment? getNullCheckHint(Source? source, Expression expression) {
return (_nullCheckHints[source] ??
{})[(uniqueIdentifierForSpan(expression.offset, expression.end))];
}
/// If the given [node] is preceded by a `/*required*/` hint, returns the
/// HintComment for it; otherwise returns `null`. See [recordRequiredHint].
- HintComment getRequiredHint(Source source, FormalParameter node) {
+ HintComment? getRequiredHint(Source? source, FormalParameter node) {
return (_requiredHints[source] ?? {})[node.offset];
}
/// Records conditional discard information for the given AST node (which is
/// an `if` statement or a conditional (`?:`) expression).
void recordConditionalDiscard(
- Source source, AstNode node, ConditionalDiscard conditionalDiscard) {
+ Source? source, AstNode node, ConditionalDiscard conditionalDiscard) {
(_conditionalDiscards[source] ??= {})[node.offset] = conditionalDiscard;
}
/// Associates a [class_] with decorated type information for the superclasses
/// it directly implements/extends/etc.
void recordDecoratedDirectSupertypes(ClassElement class_,
- Map<ClassElement, DecoratedType> decoratedDirectSupertypes) {
+ Map<ClassElement, DecoratedType?> decoratedDirectSupertypes) {
_decoratedDirectSupertypes[class_] = decoratedDirectSupertypes;
}
/// Associates decorated type information with the given [element].
- void recordDecoratedElementType(Element element, DecoratedType type) {
+ void recordDecoratedElementType(Element? element, DecoratedType? type) {
assert(() {
assert(element is! TypeParameterElement,
'Use recordDecoratedTypeParameterBound instead');
- var library = element.library;
+ var library = element!.library;
if (library == null) {
// No problem; the element is probably a parameter of a function type
// expressed using new-style Function syntax.
@@ -230,11 +231,11 @@
}
/// Associates decorated type information with the given expression [node].
- void recordDecoratedExpressionType(Expression node, DecoratedType type) {}
+ void recordDecoratedExpressionType(Expression node, DecoratedType? type) {}
/// Associates decorated type information with the given [type] node.
void recordDecoratedTypeAnnotation(
- Source source, TypeAnnotation node, DecoratedType type) {
+ Source? source, TypeAnnotation node, DecoratedType type) {
instrumentation?.explicitTypeNullability(source, node, type.node);
var id = uniqueIdentifierForSpan(node.offset, node.end);
(_decoratedTypeAnnotations[source] ??= {})[id] = type;
@@ -242,7 +243,7 @@
/// Associates a set of nullability checks with the given expression [node].
void recordExpressionChecks(
- Source source, Expression expression, ExpressionChecksOrigin origin) {
+ Source? source, Expression expression, ExpressionChecksOrigin origin) {
(_expressionChecks[source] ??=
{})[uniqueIdentifierForSpan(expression.offset, expression.end)] =
origin.checks;
@@ -250,13 +251,13 @@
/// Records that the given [node] was preceded by a `/*late*/` hint.
void recordLateHint(
- Source source, VariableDeclarationList node, HintComment hint) {
+ Source? source, VariableDeclarationList node, HintComment hint) {
(_lateHints[source] ??= {})[node.offset] = hint;
}
/// Records that the given [node] was followed by a `/*?*/` or `/*!*/` hint.
void recordNullabilityHint(
- Source source, AstNode node, HintComment hintComment) {
+ Source? source, AstNode node, HintComment hintComment) {
assert(node is TypeAnnotation ||
node is FunctionTypedFormalParameter ||
(node is FieldFormalParameter && node.parameters != null));
@@ -267,7 +268,7 @@
/// Records that the given [expression] is followed by a null check hint
/// (`/*!*/`), for later recall by [hasNullCheckHint].
void recordNullCheckHint(
- Source source, Expression expression, HintComment hintComment) {
+ Source? source, Expression expression, HintComment hintComment) {
(_nullCheckHints[source] ??=
{})[uniqueIdentifierForSpan(expression.offset, expression.end)] =
hintComment;
@@ -275,13 +276,13 @@
/// Records that the given [node] was preceded by a `/*required*/` hint.
void recordRequiredHint(
- Source source, FormalParameter node, HintComment hint) {
+ Source? source, FormalParameter node, HintComment hint) {
(_requiredHints[source] ??= {})[node.offset] = hint;
}
/// Records the fact that prior to migration, an unnecessary cast existed at
/// [node].
- void recordUnnecessaryCast(Source source, AsExpression node) {
+ void recordUnnecessaryCast(Source? source, AsExpression node) {
bool newlyAdded = (_unnecessaryCasts[source] ??= {})
.add(uniqueIdentifierForSpan(node.offset, node.end));
assert(newlyAdded);
@@ -294,10 +295,10 @@
/// the nullabilities associated with nullability nodes to determine which
/// types should be nullable and which types should not.
DartType toFinalType(DecoratedType decoratedType) {
- var type = decoratedType.type;
+ var type = decoratedType.type!;
if (type.isVoid || type.isDynamic) return type;
if (type is NeverType) {
- if (decoratedType.node.isNullable) {
+ if (decoratedType.node!.isNullable) {
return (_typeProvider.nullType as TypeImpl)
.withNullability(NullabilitySuffix.none);
} else {
@@ -307,7 +308,7 @@
return (_typeProvider.nullType as TypeImpl)
.withNullability(NullabilitySuffix.none);
}
- var nullabilitySuffix = decoratedType.node.isNullable
+ var nullabilitySuffix = decoratedType.node!.isNullable
? NullabilitySuffix.question
: NullabilitySuffix.none;
if (type is FunctionType) {
@@ -315,33 +316,33 @@
for (int i = 0; i < type.parameters.length; i++) {
var origParameter = type.parameters[i];
ParameterKind parameterKind;
- DecoratedType parameterType;
+ DecoratedType? parameterType;
var name = origParameter.name;
if (origParameter.isNamed) {
// TODO(paulberry): infer ParameterKind.NAMED_REQUIRED when
// appropriate. See https://github.com/dart-lang/sdk/issues/38596.
parameterKind = ParameterKind.NAMED;
- parameterType = decoratedType.namedParameters[name];
+ parameterType = decoratedType.namedParameters![name];
} else {
parameterKind = origParameter.isOptional
? ParameterKind.POSITIONAL
: ParameterKind.REQUIRED;
- parameterType = decoratedType.positionalParameters[i];
+ parameterType = decoratedType.positionalParameters![i];
}
parameters.add(ParameterElementImpl.synthetic(
- name, toFinalType(parameterType), parameterKind));
+ name, toFinalType(parameterType!), parameterKind));
}
return FunctionTypeImpl(
typeFormals: type.typeFormals,
parameters: parameters,
- returnType: toFinalType(decoratedType.returnType),
+ returnType: toFinalType(decoratedType.returnType!),
nullabilitySuffix: nullabilitySuffix,
);
} else if (type is InterfaceType) {
return InterfaceTypeImpl(
element: type.element,
typeArguments: [
- for (var arg in decoratedType.typeArguments) toFinalType(arg)
+ for (var arg in decoratedType.typeArguments) toFinalType(arg!)
],
nullabilitySuffix: nullabilitySuffix,
);
@@ -360,14 +361,14 @@
/// Queries whether, prior to migration, an unnecessary cast existed at
/// [node].
- bool wasUnnecessaryCast(Source source, AsExpression node) =>
+ bool wasUnnecessaryCast(Source? source, AsExpression node) =>
(_unnecessaryCasts[source] ?? const {})
.contains(uniqueIdentifierForSpan(node.offset, node.end));
/// Creates a decorated type for the given [element], which should come from
/// an already-migrated library (or the SDK).
DecoratedType _createDecoratedElementType(Element element) {
- if (_graph.isBeingMigrated(element.library.source) &&
+ if (_graph.isBeingMigrated(element.library!.source) &&
!_isLoadLibraryElement(element)) {
var description;
if (ElementTypeProvider.current is MigrationResolutionHooksImpl) {
@@ -383,7 +384,7 @@
DecoratedType decoratedType;
if (element is Member) {
- assert((element as Member).isLegacy);
+ assert(element.isLegacy);
element = element.declaration;
}
@@ -392,7 +393,7 @@
// case) `Function(T)`. Without this we would get `Function<T>(T)` which
// is incorrect. This is a known issue with `.type` on typedefs in the
// analyzer.
- element = (element as TypeAliasElement).aliasedElement;
+ element = element.aliasedElement!;
}
var target = NullabilityNodeTarget.element(element, _getLineInfo);
@@ -503,7 +504,7 @@
}
extension on TypeParameterElement {
- DartType get preMigrationBound {
+ DartType? get preMigrationBound {
var previousElementTypeProvider = ElementTypeProvider.current;
try {
ElementTypeProvider.current = const ElementTypeProvider();
diff --git a/pkg/nnbd_migration/pubspec.yaml b/pkg/nnbd_migration/pubspec.yaml
index 5d64dae..18fffd3 100644
--- a/pkg/nnbd_migration/pubspec.yaml
+++ b/pkg/nnbd_migration/pubspec.yaml
@@ -4,7 +4,7 @@
publish_to: none
environment:
- sdk: '>=2.6.0 <3.0.0'
+ sdk: '>=2.12.0 <3.0.0'
dependencies:
_fe_analyzer_shared:
@@ -16,10 +16,8 @@
args: ^1.4.4
charcode: ^1.1.0
cli_util: ^0.2.0
- collection: ^1.14.11
+ collection: ^1.15.0-nullsafety.4
crypto: ^2.0.6
- dartdev:
- path: ../dartdev
meta:
path: ../meta
path: ^1.6.2
diff --git a/pkg/nnbd_migration/test/abstract_context.dart b/pkg/nnbd_migration/test/abstract_context.dart
index 1eafda1c..1b2b8a3 100644
--- a/pkg/nnbd_migration/test/abstract_context.dart
+++ b/pkg/nnbd_migration/test/abstract_context.dart
@@ -20,10 +20,10 @@
/// TODO(paulberry): this logic is duplicated from other packages. Find a way
/// share it, or avoid relying on it.
class AbstractContextTest with ResourceProviderMixin {
- OverlayResourceProvider overlayResourceProvider;
+ OverlayResourceProvider? overlayResourceProvider;
- AnalysisContextCollectionImpl _analysisContextCollection;
- AnalysisDriver _driver;
+ AnalysisContextCollectionImpl? _analysisContextCollection;
+ AnalysisDriver? _driver;
final Set<String> knownPackages = {};
@@ -32,7 +32,7 @@
/// `false` by default. May be overridden in derived test classes.
bool get analyzeWithNnbd => false;
- AnalysisDriver get driver {
+ AnalysisDriver? get driver {
if (_driver == null) {
_createAnalysisContexts();
}
@@ -41,7 +41,7 @@
String get homePath => '/home';
- AnalysisSession get session => driver.currentSession;
+ AnalysisSession get session => driver!.currentSession;
String get testsPath => '$homePath/tests';
@@ -79,11 +79,11 @@
''');
}
- Source addSource(String path, String content, [Uri uri]) {
+ Source addSource(String path, String content, [Uri? uri]) {
File file = newFile(path, content: content);
Source source = file.createSource(uri);
- driver.addFile(file.path);
- driver.changeFile(file.path);
+ driver!.addFile(file.path);
+ driver!.changeFile(file.path);
return source;
}
@@ -193,6 +193,6 @@
_createAnalysisContexts();
}
path = convertPath(path);
- return _analysisContextCollection.contextFor(path);
+ return _analysisContextCollection!.contextFor(path);
}
}
diff --git a/pkg/nnbd_migration/test/abstract_single_unit.dart b/pkg/nnbd_migration/test/abstract_single_unit.dart
index 1edbb80..dc9049a 100644
--- a/pkg/nnbd_migration/test/abstract_single_unit.dart
+++ b/pkg/nnbd_migration/test/abstract_single_unit.dart
@@ -19,18 +19,18 @@
class AbstractSingleUnitTest extends AbstractContextTest {
bool verifyNoTestUnitErrors = true;
- String testCode;
- String testFile;
- Uri testUri;
- Source testSource;
- ResolvedUnitResult testAnalysisResult;
- CompilationUnit testUnit;
- CompilationUnitElement testUnitElement;
- LibraryElement testLibraryElement;
- FindNode findNode;
- FindElement findElement;
+ String? testCode;
+ late String testFile;
+ Uri? testUri;
+ Source? testSource;
+ late ResolvedUnitResult testAnalysisResult;
+ CompilationUnit? testUnit;
+ CompilationUnitElement? testUnitElement;
+ LibraryElement? testLibraryElement;
+ late FindNode findNode;
+ late FindElement findElement;
- void addTestSource(String code, [Uri uri]) {
+ void addTestSource(String code, [Uri? uri]) {
testCode = code;
testSource = addSource(testFile, code, uri);
}
@@ -52,10 +52,10 @@
error.errorCode != HintCode.UNUSED_LOCAL_VARIABLE;
}), isEmpty);
}
- testUnitElement = testUnit.declaredElement;
- testLibraryElement = testUnitElement.library;
- findNode = FindNode(code, testUnit);
- findElement = FindElement(testUnit);
+ testUnitElement = testUnit!.declaredElement;
+ testLibraryElement = testUnitElement!.library;
+ findNode = FindNode(code, testUnit!);
+ findElement = FindElement(testUnit!);
}
@override
diff --git a/pkg/nnbd_migration/test/already_migrated_code_decorator_test.dart b/pkg/nnbd_migration/test/already_migrated_code_decorator_test.dart
index 57d6a9e..d89b551 100644
--- a/pkg/nnbd_migration/test/already_migrated_code_decorator_test.dart
+++ b/pkg/nnbd_migration/test/already_migrated_code_decorator_test.dart
@@ -61,39 +61,39 @@
void checkAlwaysNullable(NullabilityNode node, String displayName) {
var edge = assertEdge(always, node, hard: true, checkable: false);
- var origin = graph.getEdgeOrigin(edge);
+ var origin = graph.getEdgeOrigin(edge)!;
expect(origin.kind, EdgeOriginKind.alwaysNullableType);
expect(origin.element, same(element));
expect(node.displayName, displayName);
}
- void checkDynamic(DecoratedType decoratedType, String displayName) {
- expect(decoratedType.type, same(typeProvider.dynamicType));
- checkAlwaysNullable(decoratedType.node, displayName);
+ void checkDynamic(DecoratedType? decoratedType, String displayName) {
+ expect(decoratedType!.type, same(typeProvider.dynamicType));
+ checkAlwaysNullable(decoratedType.node!, displayName);
}
- void checkExplicitlyNonNullable(NullabilityNode node, String displayName) {
+ void checkExplicitlyNonNullable(NullabilityNode? node, String displayName) {
var edge = assertEdge(node, never, hard: true, checkable: false);
- var origin = graph.getEdgeOrigin(edge);
+ var origin = graph.getEdgeOrigin(edge)!;
expect(origin.kind, EdgeOriginKind.alreadyMigratedType);
expect(origin.element, same(element));
- expect(node.displayName, displayName);
+ expect(node!.displayName, displayName);
}
- void checkExplicitlyNullable(NullabilityNode node, String displayName) {
+ void checkExplicitlyNullable(NullabilityNode? node, String displayName) {
var edge = assertEdge(always, node, hard: true, checkable: false);
- var origin = graph.getEdgeOrigin(edge);
+ var origin = graph.getEdgeOrigin(edge)!;
expect(origin.kind, EdgeOriginKind.alreadyMigratedType);
expect(origin.element, same(element));
- expect(node.displayName, displayName);
+ expect(node!.displayName, displayName);
}
void checkFutureOr(
DecoratedType decoratedType,
- void Function(NullabilityNode, String) checkNullability,
- void Function(DecoratedType, String) checkArgument,
+ void Function(NullabilityNode?, String) checkNullability,
+ void Function(DecoratedType?, String) checkArgument,
String displayName) {
- expect(decoratedType.type.element, typeProvider.futureOrElement);
+ expect(decoratedType.type!.element, typeProvider.futureOrElement);
checkNullability(decoratedType.node, displayName);
checkArgument(
decoratedType.typeArguments[0], 'type argument 0 of $displayName');
@@ -101,19 +101,19 @@
void checkInt(
DecoratedType decoratedType,
- void Function(NullabilityNode, String) checkNullability,
+ void Function(NullabilityNode?, String) checkNullability,
String displayName) {
- expect(decoratedType.type.element, typeProvider.intType.element);
+ expect(decoratedType.type!.element, typeProvider.intType.element);
checkNullability(decoratedType.node, displayName);
}
void checkIterable(
DecoratedType decoratedType,
- void Function(NullabilityNode, String) checkNullability,
- void Function(DecoratedType, String) checkArgument,
+ void Function(NullabilityNode?, String) checkNullability,
+ void Function(DecoratedType?, String) checkArgument,
String displayName) {
expect(
- decoratedType.type.element, typeProvider.iterableDynamicType.element);
+ decoratedType.type!.element, typeProvider.iterableDynamicType.element);
checkNullability(decoratedType.node, displayName);
checkArgument(
decoratedType.typeArguments[0], 'type argument 0 of $displayName');
@@ -126,23 +126,23 @@
void checkNum(
DecoratedType decoratedType,
- void Function(NullabilityNode, String) checkNullability,
+ void Function(NullabilityNode?, String) checkNullability,
String displayName) {
- expect(decoratedType.type.element, typeProvider.numType.element);
+ expect(decoratedType.type!.element, typeProvider.numType.element);
checkNullability(decoratedType.node, displayName);
}
void checkObject(
DecoratedType decoratedType,
- void Function(NullabilityNode, String) checkNullability,
+ void Function(NullabilityNode?, String) checkNullability,
String displayName) {
- expect(decoratedType.type.element, typeProvider.objectType.element);
+ expect(decoratedType.type!.element, typeProvider.objectType.element);
checkNullability(decoratedType.node, displayName);
}
void checkTypeParameter(
DecoratedType decoratedType,
- void Function(NullabilityNode, String) checkNullability,
+ void Function(NullabilityNode?, String) checkNullability,
TypeParameterElement expectedElement,
String displayName) {
var type = decoratedType.type as TypeParameterTypeImpl;
@@ -152,7 +152,7 @@
void checkVoid(DecoratedType decoratedType, String displayName) {
expect(decoratedType.type, same(typeProvider.voidType));
- checkAlwaysNullable(decoratedType.node, displayName);
+ checkAlwaysNullable(decoratedType.node!, displayName);
}
DecoratedType decorate(DartType type) {
@@ -162,7 +162,7 @@
return decoratedType;
}
- DecoratedType getDecoratedBound(TypeParameterElement element) =>
+ DecoratedType? getDecoratedBound(TypeParameterElement element) =>
decoratedTypeParameterBounds.get(element);
void setUp() {
@@ -191,9 +191,9 @@
nullabilitySuffix: suffix,
),
);
- checkNum(getDecoratedBound(typeFormal), checkExplicitlyNonNullable,
+ checkNum(getDecoratedBound(typeFormal)!, checkExplicitlyNonNullable,
'bound of type formal T of test type');
- checkTypeParameter(decoratedType.returnType, checkExplicitlyNonNullable,
+ checkTypeParameter(decoratedType.returnType!, checkExplicitlyNonNullable,
typeFormal, 'return type of test type');
}
@@ -210,9 +210,9 @@
nullabilitySuffix: suffix,
),
);
- checkObject(getDecoratedBound(typeFormal), checkExplicitlyNullable,
+ checkObject(getDecoratedBound(typeFormal)!, checkExplicitlyNullable,
'bound of type formal T of test type');
- checkTypeParameter(decoratedType.returnType, checkExplicitlyNonNullable,
+ checkTypeParameter(decoratedType.returnType!, checkExplicitlyNonNullable,
typeFormal, 'return type of test type');
}
@@ -231,7 +231,7 @@
returnType: typeProvider.voidType,
nullabilitySuffix: suffix,
),
- ).namedParameters['x'],
+ ).namedParameters!['x'],
'parameter x of test type');
}
@@ -256,9 +256,9 @@
),
);
checkDynamic(
- decoratedType.positionalParameters[0], 'parameter 0 of test type');
+ decoratedType.positionalParameters![0], 'parameter 0 of test type');
checkDynamic(
- decoratedType.positionalParameters[1], 'parameter 1 of test type');
+ decoratedType.positionalParameters![1], 'parameter 1 of test type');
}
void test_decorate_functionType_positional_parameter() {
@@ -276,7 +276,7 @@
returnType: typeProvider.voidType,
nullabilitySuffix: suffix,
),
- ).positionalParameters[0],
+ ).positionalParameters![0],
'parameter 0 of test type');
}
@@ -324,9 +324,9 @@
element: typeProvider.mapElement,
typeArguments: [typeProvider.intType, typeProvider.numType],
nullabilitySuffix: suffix));
- checkInt(decoratedType.typeArguments[0], checkExplicitlyNonNullable,
+ checkInt(decoratedType.typeArguments[0]!, checkExplicitlyNonNullable,
'type argument 0 of test type');
- checkNum(decoratedType.typeArguments[1], checkExplicitlyNonNullable,
+ checkNum(decoratedType.typeArguments[1]!, checkExplicitlyNonNullable,
'type argument 1 of test type');
}
@@ -408,7 +408,7 @@
decoratedSupertypes[1],
checkExplicitlyNonNullable,
(t, displayName) => checkTypeParameter(
- t, checkExplicitlyNonNullable, typeParam, displayName),
+ t!, checkExplicitlyNonNullable, typeParam, displayName),
'Future (async:1:1)');
}
@@ -429,7 +429,7 @@
decoratedSupertypes[0],
checkExplicitlyNonNullable,
(type, displayName) => checkTypeParameter(
- type, checkExplicitlyNonNullable, t, displayName),
+ type!, checkExplicitlyNonNullable, t, displayName),
'C (test.dart:1:1)');
}
@@ -525,10 +525,10 @@
class _LibraryElementMock implements LibraryElementImpl {
@override
- CompilationUnitElement definingCompilationUnit;
+ late CompilationUnitElement definingCompilationUnit;
@override
- Source source;
+ late Source source;
_LibraryElementMock() {
source = _SourceMock();
@@ -536,7 +536,7 @@
}
@override
- Element get enclosingElement => null;
+ Element? get enclosingElement => null;
@override
bool get isNonNullableByDefault => false;
diff --git a/pkg/nnbd_migration/test/api_test.dart b/pkg/nnbd_migration/test/api_test.dart
index 4675cb9..6d1fe57 100644
--- a/pkg/nnbd_migration/test/api_test.dart
+++ b/pkg/nnbd_migration/test/api_test.dart
@@ -30,7 +30,7 @@
/// Base class for provisional API tests.
abstract class _ProvisionalApiTestBase extends AbstractContextTest {
- String projectPath;
+ String? projectPath;
bool get _usePermissiveMode;
@@ -54,10 +54,10 @@
bool warnOnWeakCode = false,
bool allowErrors = false}) async {
for (var path in migratedInput.keys) {
- newFile(path, content: migratedInput[path]);
+ newFile(path, content: migratedInput[path]!);
}
for (var path in input.keys) {
- newFile(path, content: input[path]);
+ newFile(path, content: input[path]!);
}
var listener = TestMigrationListener();
var migration = NullabilityMigration(listener, getLineInfo,
@@ -67,7 +67,7 @@
for (var path in input.keys) {
var resolvedLibrary = await session.getResolvedLibrary2(path);
if (resolvedLibrary is ResolvedLibraryResult) {
- for (var unit in resolvedLibrary.units) {
+ for (var unit in resolvedLibrary.units!) {
var errors =
unit.errors.where((e) => e.severity == Severity.error).toList();
if (!allowErrors && errors.isNotEmpty) {
@@ -82,7 +82,7 @@
for (var path in input.keys) {
var resolvedLibrary = await session.getResolvedLibrary2(path);
if (resolvedLibrary is ResolvedLibraryResult) {
- for (var unit in resolvedLibrary.units) {
+ for (var unit in resolvedLibrary.units!) {
migration.processInput(unit);
}
}
@@ -91,7 +91,7 @@
for (var path in input.keys) {
var resolvedLibrary = await session.getResolvedLibrary2(path);
if (resolvedLibrary is ResolvedLibraryResult) {
- for (var unit in resolvedLibrary.units) {
+ for (var unit in resolvedLibrary.units!) {
migration.finalizeInput(unit);
}
}
@@ -106,7 +106,7 @@
for (var path in expectedOutput.keys) {
var sourceEditsForPath = sourceEdits[path] ?? [];
sourceEditsForPath.sort((a, b) => b.offset.compareTo(a.offset));
- expect(SourceEdit.applySequence(input[path], sourceEditsForPath),
+ expect(SourceEdit.applySequence(input[path]!, sourceEditsForPath),
expectedOutput[path]);
}
}
@@ -8421,6 +8421,6 @@
@override
void _betweenStages() {
- driver.clearLibraryContext();
+ driver!.clearLibraryContext();
}
}
diff --git a/pkg/nnbd_migration/test/api_test_base.dart b/pkg/nnbd_migration/test/api_test_base.dart
index 664a411..0e66b16 100644
--- a/pkg/nnbd_migration/test/api_test_base.dart
+++ b/pkg/nnbd_migration/test/api_test_base.dart
@@ -23,7 +23,7 @@
@override
void reportException(
- Source source, AstNode node, Object exception, StackTrace stackTrace) {
+ Source? source, AstNode? node, Object exception, StackTrace stackTrace) {
fail('Exception reported: $exception\n$stackTrace');
}
}
diff --git a/pkg/nnbd_migration/test/decorated_class_hierarchy_test.dart b/pkg/nnbd_migration/test/decorated_class_hierarchy_test.dart
index f3fdf5e..15a4dcd 100644
--- a/pkg/nnbd_migration/test/decorated_class_hierarchy_test.dart
+++ b/pkg/nnbd_migration/test/decorated_class_hierarchy_test.dart
@@ -19,7 +19,7 @@
@reflectiveTest
class DecoratedClassHierarchyTest extends MigrationVisitorTestBase {
- DecoratedClassHierarchy _hierarchy;
+ late DecoratedClassHierarchy _hierarchy;
@override
Future<CompilationUnit> analyze(String code) async {
@@ -37,14 +37,14 @@
var decoratedType = decoratedTypeAnnotation('Derived<int>');
var asInstanceOfBase =
_hierarchy.asInstanceOf(decoratedType, findElement.class_('Base'));
- _assertType(asInstanceOfBase.type, 'Base<List<int>>');
+ _assertType(asInstanceOfBase.type!, 'Base<List<int>>');
expect(asInstanceOfBase.node, same(decoratedType.node));
var listOfUType = decoratedTypeAnnotation('List<U>');
- expect(asInstanceOfBase.typeArguments[0].node, same(listOfUType.node));
- var substitution = asInstanceOfBase.typeArguments[0].typeArguments[0].node
+ expect(asInstanceOfBase.typeArguments[0]!.node, same(listOfUType.node));
+ var substitution = asInstanceOfBase.typeArguments[0]!.typeArguments[0]!.node
as NullabilityNodeForSubstitution;
- expect(substitution.innerNode, same(decoratedType.typeArguments[0].node));
- expect(substitution.outerNode, same(listOfUType.typeArguments[0].node));
+ expect(substitution.innerNode, same(decoratedType.typeArguments[0]!.node));
+ expect(substitution.outerNode, same(listOfUType.typeArguments[0]!.node));
}
Future<void> test_getDecoratedSupertype_complex() async {
@@ -60,24 +60,24 @@
var mapRef = decoratedTypeAnnotation('Map');
var intRef = decoratedTypeAnnotation('int');
var vRef = decoratedTypeAnnotation('V>>');
- _assertType(decoratedSupertype.type, 'Base<List<Map<int, V>>>');
+ _assertType(decoratedSupertype.type!, 'Base<List<Map<int, V>>>');
expect(decoratedSupertype.node, same(never));
var baseArgs = decoratedSupertype.typeArguments;
expect(baseArgs, hasLength(1));
- _assertType(baseArgs[0].type, 'List<Map<int, V>>');
- expect(baseArgs[0].node, same(listRef.node));
- var listArgs = baseArgs[0].typeArguments;
+ _assertType(baseArgs[0]!.type!, 'List<Map<int, V>>');
+ expect(baseArgs[0]!.node, same(listRef.node));
+ var listArgs = baseArgs[0]!.typeArguments;
expect(listArgs, hasLength(1));
- _assertType(listArgs[0].type, 'Map<int, V>');
- var mapNode = listArgs[0].node as NullabilityNodeForSubstitution;
+ _assertType(listArgs[0]!.type!, 'Map<int, V>');
+ var mapNode = listArgs[0]!.node as NullabilityNodeForSubstitution;
expect(mapNode.innerNode, same(mapRef.node));
expect(mapNode.outerNode, same(uRef.node));
- var mapArgs = listArgs[0].typeArguments;
+ var mapArgs = listArgs[0]!.typeArguments;
expect(mapArgs, hasLength(2));
- _assertType(mapArgs[0].type, 'int');
- expect(mapArgs[0].node, same(intRef.node));
- _assertType(mapArgs[1].type, 'V');
- expect(mapArgs[1].node, same(vRef.node));
+ _assertType(mapArgs[0]!.type!, 'int');
+ expect(mapArgs[0]!.node, same(intRef.node));
+ _assertType(mapArgs[1]!.type!, 'V');
+ expect(mapArgs[1]!.node, same(vRef.node));
}
Future<void> test_getDecoratedSupertype_extends_simple() async {
@@ -89,13 +89,13 @@
findElement.class_('Derived'), findElement.class_('Base'));
var vRef = decoratedTypeAnnotation('V, W> {');
var wRef = decoratedTypeAnnotation('W> {');
- _assertType(decoratedSupertype.type, 'Base<V, W>');
+ _assertType(decoratedSupertype.type!, 'Base<V, W>');
expect(decoratedSupertype.node, same(never));
expect(decoratedSupertype.typeArguments, hasLength(2));
- _assertType(decoratedSupertype.typeArguments[0].type, 'V');
- expect(decoratedSupertype.typeArguments[0].node, same(vRef.node));
- _assertType(decoratedSupertype.typeArguments[1].type, 'W');
- expect(decoratedSupertype.typeArguments[1].node, same(wRef.node));
+ _assertType(decoratedSupertype.typeArguments[0]!.type!, 'V');
+ expect(decoratedSupertype.typeArguments[0]!.node, same(vRef.node));
+ _assertType(decoratedSupertype.typeArguments[1]!.type!, 'W');
+ expect(decoratedSupertype.typeArguments[1]!.node, same(wRef.node));
}
Future<void> test_getDecoratedSupertype_implements_simple() async {
@@ -107,13 +107,13 @@
findElement.class_('Derived'), findElement.class_('Base'));
var vRef = decoratedTypeAnnotation('V, W> {');
var wRef = decoratedTypeAnnotation('W> {');
- _assertType(decoratedSupertype.type, 'Base<V, W>');
+ _assertType(decoratedSupertype.type!, 'Base<V, W>');
expect(decoratedSupertype.node, same(never));
expect(decoratedSupertype.typeArguments, hasLength(2));
- _assertType(decoratedSupertype.typeArguments[0].type, 'V');
- expect(decoratedSupertype.typeArguments[0].node, same(vRef.node));
- _assertType(decoratedSupertype.typeArguments[1].type, 'W');
- expect(decoratedSupertype.typeArguments[1].node, same(wRef.node));
+ _assertType(decoratedSupertype.typeArguments[0]!.type!, 'V');
+ expect(decoratedSupertype.typeArguments[0]!.node, same(vRef.node));
+ _assertType(decoratedSupertype.typeArguments[1]!.type!, 'W');
+ expect(decoratedSupertype.typeArguments[1]!.node, same(wRef.node));
}
Future<void> test_getDecoratedSupertype_not_generic() async {
@@ -123,7 +123,7 @@
''');
var decoratedSupertype = _hierarchy.getDecoratedSupertype(
findElement.class_('Derived'), findElement.class_('Base'));
- _assertType(decoratedSupertype.type, 'Base');
+ _assertType(decoratedSupertype.type!, 'Base');
expect(decoratedSupertype.node, same(never));
expect(decoratedSupertype.typeArguments, isEmpty);
}
@@ -137,13 +137,13 @@
findElement.mixin('Derived'), findElement.class_('Base'));
var vRef = decoratedTypeAnnotation('V, W> {');
var wRef = decoratedTypeAnnotation('W> {');
- _assertType(decoratedSupertype.type, 'Base<V, W>');
+ _assertType(decoratedSupertype.type!, 'Base<V, W>');
expect(decoratedSupertype.node, same(never));
expect(decoratedSupertype.typeArguments, hasLength(2));
- _assertType(decoratedSupertype.typeArguments[0].type, 'V');
- expect(decoratedSupertype.typeArguments[0].node, same(vRef.node));
- _assertType(decoratedSupertype.typeArguments[1].type, 'W');
- expect(decoratedSupertype.typeArguments[1].node, same(wRef.node));
+ _assertType(decoratedSupertype.typeArguments[0]!.type!, 'V');
+ expect(decoratedSupertype.typeArguments[0]!.node, same(vRef.node));
+ _assertType(decoratedSupertype.typeArguments[1]!.type!, 'W');
+ expect(decoratedSupertype.typeArguments[1]!.node, same(wRef.node));
}
Future<void> test_getDecoratedSupertype_unrelated_type() async {
@@ -166,13 +166,13 @@
findElement.class_('Derived'), findElement.class_('Base'));
var vRef = decoratedTypeAnnotation('V, W> {');
var wRef = decoratedTypeAnnotation('W> {');
- _assertType(decoratedSupertype.type, 'Base<V, W>');
+ _assertType(decoratedSupertype.type!, 'Base<V, W>');
expect(decoratedSupertype.node, same(never));
expect(decoratedSupertype.typeArguments, hasLength(2));
- _assertType(decoratedSupertype.typeArguments[0].type, 'V');
- expect(decoratedSupertype.typeArguments[0].node, same(vRef.node));
- _assertType(decoratedSupertype.typeArguments[1].type, 'W');
- expect(decoratedSupertype.typeArguments[1].node, same(wRef.node));
+ _assertType(decoratedSupertype.typeArguments[0]!.type!, 'V');
+ expect(decoratedSupertype.typeArguments[0]!.node, same(vRef.node));
+ _assertType(decoratedSupertype.typeArguments[1]!.type!, 'W');
+ expect(decoratedSupertype.typeArguments[1]!.node, same(wRef.node));
}
void _assertType(DartType type, String expected) {
diff --git a/pkg/nnbd_migration/test/decorated_type_test.dart b/pkg/nnbd_migration/test/decorated_type_test.dart
index 4a18071f..3f28996 100644
--- a/pkg/nnbd_migration/test/decorated_type_test.dart
+++ b/pkg/nnbd_migration/test/decorated_type_test.dart
@@ -342,7 +342,7 @@
function(dynamic_, typeFormals: [t], node: never)) as FunctionType;
assertDartType(type, 'dynamic Function<T extends dynamic>()');
assertDartType(
- elementTypeProvider.getTypeParameterBound(type.typeFormals[0]),
+ elementTypeProvider.getTypeParameterBound(type.typeFormals[0])!,
'dynamic');
}
@@ -352,7 +352,8 @@
function(dynamic_, typeFormals: [t], node: never)) as FunctionType;
assertDartType(type, 'dynamic Function<T extends num?>()');
assertDartType(
- elementTypeProvider.getTypeParameterBound(type.typeFormals[0]), 'num?');
+ elementTypeProvider.getTypeParameterBound(type.typeFormals[0])!,
+ 'num?');
}
void test_toFinalType_function_generic_bound_object_question() {
@@ -361,7 +362,7 @@
function(dynamic_, typeFormals: [t], node: never)) as FunctionType;
assertDartType(type, 'dynamic Function<T extends Object?>()');
assertDartType(
- elementTypeProvider.getTypeParameterBound(type.typeFormals[0]),
+ elementTypeProvider.getTypeParameterBound(type.typeFormals[0])!,
'Object?');
}
@@ -626,11 +627,11 @@
void freshTypeParameterCreated(TypeParameterElement newTypeParameter,
TypeParameterElement oldTypeParameter) {
- DecoratedTypeParameterBounds.current.put(newTypeParameter,
- DecoratedTypeParameterBounds.current.get(oldTypeParameter));
+ DecoratedTypeParameterBounds.current!.put(newTypeParameter,
+ DecoratedTypeParameterBounds.current!.get(oldTypeParameter));
}
- DartType getTypeParameterBound(TypeParameterElement element) {
+ DartType? getTypeParameterBound(TypeParameterElement element) {
var decoratedType = variables.decoratedTypeParameterBound(element,
allowNullUnparentedBounds: true);
if (decoratedType == null) return element.bound;
diff --git a/pkg/nnbd_migration/test/edge_builder_test.dart b/pkg/nnbd_migration/test/edge_builder_test.dart
index 0771b0e..901468a 100644
--- a/pkg/nnbd_migration/test/edge_builder_test.dart
+++ b/pkg/nnbd_migration/test/edge_builder_test.dart
@@ -43,11 +43,11 @@
with EdgeTester, DecoratedTypeTester {
static const EdgeOrigin origin = _TestEdgeOrigin();
- LibraryElementImpl _myLibrary;
+ LibraryElementImpl? _myLibrary;
- ClassElement _myListOfListClass;
+ ClassElement? _myListOfListClass;
- DecoratedType _myListOfListSupertype;
+ late DecoratedType _myListOfListSupertype;
@override
final TypeProvider typeProvider;
@@ -96,14 +96,14 @@
var t = typeParameter('T', object());
_myListOfListSupertype = list(list(typeParameterType(t)));
_myListOfListClass = ClassElementImpl('MyListOfList', 0)
- ..enclosingElement = _myLibrary.definingCompilationUnit
+ ..enclosingElement = _myLibrary!.definingCompilationUnit
..typeParameters = [t]
- ..supertype = _myListOfListSupertype.type as InterfaceType;
+ ..supertype = _myListOfListSupertype.type as InterfaceType?;
}
return DecoratedType(
InterfaceTypeImpl(
- element: _myListOfListClass,
- typeArguments: [elementType.type],
+ element: _myListOfListClass!,
+ typeArguments: [elementType.type!],
nullabilitySuffix: NullabilitySuffix.star,
),
newNode(),
@@ -115,7 +115,7 @@
var t = list(object());
assign(bottom, t);
assertEdge(never, t.node, hard: false);
- assertNoEdge(anyNode, t.typeArguments[0].node);
+ assertNoEdge(anyNode, t.typeArguments[0]!.node);
}
void test_bottom_to_simple() {
@@ -132,7 +132,7 @@
assertEdge(t1.node, t2.node, hard: true);
assertEdge(t1.node, bound.node, hard: false);
// TODO(40622): Should this be a checkable edge?
- assertEdge(t1.typeArguments[0].node, bound.typeArguments[0].node,
+ assertEdge(t1.typeArguments[0]!.node, bound.typeArguments[0]!.node,
hard: false, checkable: false);
}
@@ -151,7 +151,7 @@
var t2 = function(dynamic_, named: {'x': object()});
assign(t1, t2, hard: true);
// Note: t1 and t2 are swapped due to contravariance.
- assertEdge(t2.namedParameters['x'].node, t1.namedParameters['x'].node,
+ assertEdge(t2.namedParameters!['x']!.node, t1.namedParameters!['x']!.node,
hard: false, checkable: false);
}
@@ -167,7 +167,8 @@
var t2 = function(dynamic_, positional: [object()]);
assign(t1, t2, hard: true);
// Note: t1 and t2 are swapped due to contravariance.
- assertEdge(t2.positionalParameters[0].node, t1.positionalParameters[0].node,
+ assertEdge(
+ t2.positionalParameters![0]!.node, t1.positionalParameters![0]!.node,
hard: false, checkable: false);
}
@@ -183,7 +184,8 @@
var t2 = function(dynamic_, required: [object()]);
assign(t1, t2, hard: true);
// Note: t1 and t2 are swapped due to contravariance.
- assertEdge(t2.positionalParameters[0].node, t1.positionalParameters[0].node,
+ assertEdge(
+ t2.positionalParameters![0]!.node, t1.positionalParameters![0]!.node,
hard: false, checkable: false);
}
@@ -192,7 +194,8 @@
var t2 = function(dynamic_, required: [object()]);
assign(t1, t2);
// Note: t1 and t2 are swapped due to contravariance.
- assertEdge(t2.positionalParameters[0].node, t1.positionalParameters[0].node,
+ assertEdge(
+ t2.positionalParameters![0]!.node, t1.positionalParameters![0]!.node,
hard: false, checkable: false);
}
@@ -200,7 +203,7 @@
var t1 = function(object());
var t2 = function(object());
assign(t1, t2, hard: true);
- assertEdge(t1.returnType.node, t2.returnType.node,
+ assertEdge(t1.returnType!.node, t2.returnType!.node,
hard: false, checkable: false);
}
@@ -210,7 +213,7 @@
var t1 = function(void_);
var t2 = function(object());
assign(t1, t2, hard: true);
- assertEdge(t1.returnType.node, t2.returnType.node,
+ assertEdge(t1.returnType!.node, t2.returnType!.node,
hard: false, checkable: false);
}
@@ -219,7 +222,7 @@
var t2 = futureOr(int_());
assign(t1, t2, hard: true);
assertEdge(t1.node, t2.node, hard: true);
- assertEdge(t1.typeArguments[0].node, t2.typeArguments[0].node,
+ assertEdge(t1.typeArguments[0]!.node, t2.typeArguments[0]!.node,
hard: true, checkable: false);
}
@@ -230,11 +233,12 @@
// FutureOr<int>? is nullable, so Future<int>? should be.
assertEdge(t1.node, t2.node, hard: true);
// FutureOr<int?> is nullable, so Future<int>? should be.
- assertEdge(t1.typeArguments[0].node, t2.node, hard: true);
+ assertEdge(t1.typeArguments[0]!.node, t2.node, hard: true);
// FutureOr<int?> may hold a Future<int?>, so carry that forward.
- assertEdge(t1.typeArguments[0].node, t2.typeArguments[0].node, hard: false);
+ assertEdge(t1.typeArguments[0]!.node, t2.typeArguments[0]!.node,
+ hard: false);
// FutureOr<int>? does not accept a Future<int?>, so don't draw this.
- assertNoEdge(t1.node, t2.typeArguments[0].node);
+ assertNoEdge(t1.node, t2.typeArguments[0]!.node);
}
void test_future_or_int_to_int() {
@@ -242,7 +246,7 @@
var t2 = int_();
assign(t1, t2, hard: true);
assertEdge(t1.node, t2.node, hard: true);
- assertEdge(t1.typeArguments[0].node, t2.node, hard: false);
+ assertEdge(t1.typeArguments[0]!.node, t2.node, hard: false);
}
void test_future_or_list_object_to_list_int() {
@@ -250,9 +254,9 @@
var t2 = list(int_());
assign(t1, t2, hard: true);
assertEdge(t1.node, t2.node, hard: true);
- assertEdge(t1.typeArguments[0].node, t2.node, hard: false);
+ assertEdge(t1.typeArguments[0]!.node, t2.node, hard: false);
assertEdge(
- t1.typeArguments[0].typeArguments[0].node, t2.typeArguments[0].node,
+ t1.typeArguments[0]!.typeArguments[0]!.node, t2.typeArguments[0]!.node,
hard: false);
}
@@ -261,7 +265,8 @@
var t2 = futureOr(int_());
assign(t1, t2, hard: true);
assertEdge(t1.node, t2.node, hard: true);
- assertEdge(t1.typeArguments[0].node, t2.typeArguments[0].node, hard: false);
+ assertEdge(t1.typeArguments[0]!.node, t2.typeArguments[0]!.node,
+ hard: false);
}
void test_future_or_to_future_or() {
@@ -269,14 +274,15 @@
var t2 = futureOr(int_());
assign(t1, t2, hard: true);
assertEdge(t1.node, t2.node, hard: true);
- assertEdge(t1.typeArguments[0].node, t2.typeArguments[0].node, hard: false);
+ assertEdge(t1.typeArguments[0]!.node, t2.typeArguments[0]!.node,
+ hard: false);
}
void test_generic_to_dynamic() {
var t = list(object());
assign(t, dynamic_);
assertEdge(t.node, always, hard: false);
- assertNoEdge(t.typeArguments[0].node, anyNode);
+ assertNoEdge(t.typeArguments[0]!.node, anyNode);
}
void test_generic_to_generic_downcast() {
@@ -286,11 +292,11 @@
assertEdge(t1.node, t2.node, hard: true);
// Let A, B, and C be nullability nodes such that:
// - t2 is MyListOfList<Object?A>
- var a = t2.typeArguments[0].node;
+ var a = t2.typeArguments[0]!.node;
// - t1 is List<List<Object?B>>
- var b = t1.typeArguments[0].typeArguments[0].node;
+ var b = t1.typeArguments[0]!.typeArguments[0]!.node;
// - the supertype of MyListOfList<T> is List<List<T?C>>
- var c = _myListOfListSupertype.typeArguments[0].typeArguments[0].node;
+ var c = _myListOfListSupertype.typeArguments[0]!.typeArguments[0]!.node;
// Then there should be an edge from b to substitute(a, c)
assertEdge(b, substitutionNode(a, c), hard: false);
}
@@ -301,8 +307,8 @@
var t2 = list(t);
assign(t1, t2, hard: true);
assertEdge(t1.node, t2.node, hard: true);
- var a = t1.typeArguments[0].node;
- var b = t2.typeArguments[0].node;
+ var a = t1.typeArguments[0]!.node;
+ var b = t2.typeArguments[0]!.node;
assertEdge(a, b, hard: false);
}
@@ -311,7 +317,8 @@
var t2 = list(int_());
assign(t1, t2, hard: true);
assertEdge(t1.node, t2.node, hard: true);
- assertEdge(t1.typeArguments[0].node, t2.typeArguments[0].node, hard: false);
+ assertEdge(t1.typeArguments[0]!.node, t2.typeArguments[0]!.node,
+ hard: false);
}
void test_generic_to_generic_same_element() {
@@ -319,7 +326,7 @@
var t2 = list(object());
assign(t1, t2, hard: true);
assertEdge(t1.node, t2.node, hard: true);
- assertEdge(t1.typeArguments[0].node, t2.typeArguments[0].node,
+ assertEdge(t1.typeArguments[0]!.node, t2.typeArguments[0]!.node,
hard: true, checkable: false);
}
@@ -330,11 +337,11 @@
assertEdge(t1.node, t2.node, hard: false);
// Let A, B, and C be nullability nodes such that:
// - t1 is MyListOfList<Object?A>
- var a = t1.typeArguments[0].node;
+ var a = t1.typeArguments[0]!.node;
// - t2 is List<List<Object?B>>
- var b = t2.typeArguments[0].typeArguments[0].node;
+ var b = t2.typeArguments[0]!.typeArguments[0]!.node;
// - the supertype of MyListOfList<T> is List<List<T?C>>
- var c = _myListOfListSupertype.typeArguments[0].typeArguments[0].node;
+ var c = _myListOfListSupertype.typeArguments[0]!.typeArguments[0]!.node;
// Then there should be an edge from substitute(a, c) to b.
assertEdge(substitutionNode(a, c), b, hard: false, checkable: false);
}
@@ -344,14 +351,14 @@
var t2 = object();
assign(t1, t2);
assertEdge(t1.node, t2.node, hard: false);
- assertNoEdge(t1.typeArguments[0].node, anyNode);
+ assertNoEdge(t1.typeArguments[0]!.node, anyNode);
}
void test_generic_to_void() {
var t = list(object());
assign(t, void_);
assertEdge(t.node, always, hard: false);
- assertNoEdge(t.typeArguments[0].node, anyNode);
+ assertNoEdge(t.typeArguments[0]!.node, anyNode);
}
void test_int_to_future_or_int() {
@@ -366,7 +373,7 @@
// `FutureOr<int>?` because it is a narrower type, so it is less likely to
// cause a proliferation of nullable types in the user's program.
assertEdge(t1.node, t2.node, hard: true);
- assertNoEdge(t1.node, t2.typeArguments[0].node);
+ assertNoEdge(t1.node, t2.typeArguments[0]!.node);
}
void test_iterable_object_to_list_void() {
@@ -378,7 +385,7 @@
var t = list(object());
assign(null_, t);
assertEdge(always, t.node, hard: false);
- assertNoEdge(anyNode, t.typeArguments[0].node);
+ assertNoEdge(anyNode, t.typeArguments[0]!.node);
}
void test_null_to_simple() {
@@ -426,7 +433,7 @@
assertEdge(t1.node, t2.node, hard: true);
assertEdge(bound.node, t2.node, hard: false);
// TODO(40622): Should this be a checkable edge?
- assertEdge(bound.typeArguments[0].node, t2.typeArguments[0].node,
+ assertEdge(bound.typeArguments[0]!.node, t2.typeArguments[0]!.node,
hard: false, checkable: false);
}
@@ -475,8 +482,8 @@
flags: [EnableString.non_nullable],
),
);
- _myLibrary.typeSystem = typeSystem;
- _myLibrary.typeProvider = coreLibrary.typeProvider;
+ _myLibrary!.typeSystem = typeSystem;
+ _myLibrary!.typeProvider = coreLibrary.typeProvider;
var uri = Uri.parse(uriStr);
var source = _MockSource(uri);
@@ -486,7 +493,7 @@
definingUnit.librarySource = source;
definingUnit.enclosingElement = _myLibrary;
- _myLibrary.definingCompilationUnit = definingUnit;
+ _myLibrary!.definingCompilationUnit = definingUnit;
}
static void _setCoreLibrariesTypeSystem(TypeProviderImpl typeProvider) {
@@ -522,14 +529,14 @@
@reflectiveTest
class EdgeBuilderTest extends EdgeBuilderTestBase {
void assertGLB(
- NullabilityNode node, NullabilityNode left, NullabilityNode right) {
+ NullabilityNode? node, NullabilityNode? left, NullabilityNode? right) {
expect(node, isNot(TypeMatcher<NullabilityNodeForLUB>()));
assertEdge(left, node, hard: false, guards: [right]);
assertEdge(node, left, hard: false);
assertEdge(node, right, hard: false);
}
- void assertLUB(NullabilityNode node, Object left, Object right) {
+ void assertLUB(NullabilityNode node, Object? left, Object? right) {
var conditionalNode = node as NullabilityNodeForLUB;
var leftMatcher = NodeMatcher(left);
var rightMatcher = NodeMatcher(right);
@@ -539,7 +546,7 @@
/// Checks that there are no nullability nodes upstream from [node] that could
/// cause it to become nullable.
- void assertNoUpstreamNullability(NullabilityNode node) {
+ void assertNoUpstreamNullability(NullabilityNode? node) {
// Store `neverClosure` in a local variable so that we avoid the
// computational expense of recomputing it each time through the loop below.
var neverClosure = this.neverClosure;
@@ -567,26 +574,26 @@
/// Gets the [ExpressionChecks] associated with the expression whose text
/// representation is [text], or `null` if the expression has no
/// [ExpressionChecks] associated with it.
- ExpressionChecksOrigin checkExpression(String text) {
- return variables.checkExpression(findNode.expression(text));
+ ExpressionChecksOrigin? checkExpression(String text) {
+ return variables!.checkExpression(findNode.expression(text));
}
/// Gets the [DecoratedType] associated with the expression whose text
/// representation is [text], or `null` if the expression has no
/// [DecoratedType] associated with it.
- DecoratedType decoratedExpressionType(String text) {
- return variables.decoratedExpressionType(findNode.expression(text));
+ DecoratedType? decoratedExpressionType(String text) {
+ return variables!.decoratedExpressionType(findNode.expression(text));
}
bool hasNullCheckHint(Expression expression) =>
- variables.getNullCheckHint(testSource, expression) != null;
+ variables!.getNullCheckHint(testSource, expression) != null;
Future<void> test_already_migrated_field() async {
await analyze('''
double f() => double.NAN;
''');
- var nanElement = typeProvider.doubleType.element.getField('NAN');
- assertEdge(variables.decoratedElementType(nanElement).node,
+ var nanElement = typeProvider.doubleType.element.getField('NAN')!;
+ assertEdge(variables!.decoratedElementType(nanElement).node,
decoratedTypeAnnotation('double f').node,
hard: false);
}
@@ -650,7 +657,7 @@
hard: true);
assertEdge(decoratedTypeAnnotation('int').node, never, hard: true);
expect(
- variables.wasUnnecessaryCast(testSource, findNode.as_('o as')), false);
+ variables!.wasUnnecessaryCast(testSource, findNode.as_('o as')), false);
}
Future<void> test_as_int_null_ok() async {
@@ -679,7 +686,7 @@
hard: true);
assertEdge(decoratedTypeAnnotation('int)').node, never, hard: true);
expect(
- variables.wasUnnecessaryCast(testSource, findNode.as_('i as')), true);
+ variables!.wasUnnecessaryCast(testSource, findNode.as_('i as')), true);
}
Future<void> test_as_side_cast() async {
@@ -717,7 +724,7 @@
assertNoEdge(anyNode, decoratedTypeAnnotation('int> a').node);
// int> a should be connected to the bound of T in A<T>, but nothing else.
expect(
- decoratedTypeAnnotation('int> a').node.downstreamEdges, hasLength(1));
+ decoratedTypeAnnotation('int> a').node!.downstreamEdges, hasLength(1));
}
Future<void> test_assert_demonstrates_non_null_intent() async {
@@ -767,7 +774,7 @@
// TODO(mfairhurst): Confirm we want this edge.
// TODO(40622): Should this be a checkable edge?
assertEdge(
- parameterType.typeArguments[0].node, boundType.typeArguments[0].node,
+ parameterType.typeArguments[0]!.node, boundType.typeArguments[0]!.node,
hard: false, checkable: false);
}
@@ -1219,7 +1226,7 @@
assertEdge(boundType.node, returnType.node, hard: false);
// TODO(40622): Should this be a checkable edge?
assertEdge(
- boundType.typeArguments[0].node, returnType.typeArguments[0].node,
+ boundType.typeArguments[0]!.node, returnType.typeArguments[0]!.node,
hard: false, checkable: false);
}
@@ -1235,8 +1242,8 @@
var listInt = decoratedTypeAnnotation('List<int>');
assertEdge(listInt.node, iterableInt.node, hard: true);
assertEdge(
- substitutionNode(listInt.typeArguments[0].node, inSet(pointsToNever)),
- iterableInt.typeArguments[0].node,
+ substitutionNode(listInt.typeArguments[0]!.node, inSet(pointsToNever)),
+ iterableInt.typeArguments[0]!.node,
hard: true,
checkable: false);
}
@@ -1250,7 +1257,7 @@
var edge = assertEdge(decoratedTypeAnnotation('int i').node,
decoratedTypeAnnotation('int j').node,
hard: true);
- var codeReference = edge.codeReference;
+ var codeReference = edge.codeReference!;
expect(codeReference, isNotNull);
expect(codeReference.path, contains('test.dart'));
expect(codeReference.line, 2);
@@ -1279,12 +1286,12 @@
hard: true);
expect(
(graph.getEdgeOrigin(targetEdge) as CompoundAssignmentOrigin)
- .node
+ .node!
.operator
.offset,
code.indexOf('+='));
assertNullCheck(
- checkExpression('z);'),
+ checkExpression('z);')!,
assertEdge(decoratedTypeAnnotation('C z').node,
decoratedTypeAnnotation('C x').node,
hard: true));
@@ -1294,14 +1301,14 @@
hard: false);
expect(
(graph.getEdgeOrigin(operatorReturnEdge) as CompoundAssignmentOrigin)
- .node
+ .node!
.operator
.offset,
code.indexOf('+='));
var fReturnEdge = assertEdge(decoratedTypeAnnotation('C operator').node,
decoratedTypeAnnotation('C f').node,
hard: false);
- assertNullCheck(checkExpression('(y += z)'), fReturnEdge);
+ assertNullCheck(checkExpression('(y += z)')!, fReturnEdge);
}
Future<void> test_assignmentExpression_compound_withSubstitution() async {
@@ -1318,12 +1325,12 @@
hard: true);
expect(
(graph.getEdgeOrigin(targetEdge) as CompoundAssignmentOrigin)
- .node
+ .node!
.operator
.offset,
code.indexOf('+='));
assertNullCheck(
- checkExpression('z);'),
+ checkExpression('z);')!,
assertEdge(decoratedTypeAnnotation('C<int> z').node,
decoratedTypeAnnotation('C<T> x').node,
hard: true));
@@ -1333,14 +1340,14 @@
hard: false);
expect(
(graph.getEdgeOrigin(operatorReturnEdge) as CompoundAssignmentOrigin)
- .node
+ .node!
.operator
.offset,
code.indexOf('+='));
var fReturnEdge = assertEdge(decoratedTypeAnnotation('C<T> operator').node,
decoratedTypeAnnotation('C<int> f').node,
hard: false);
- assertNullCheck(checkExpression('(y += z)'), fReturnEdge);
+ assertNullCheck(checkExpression('(y += z)')!, fReturnEdge);
}
Future<void> test_assignmentExpression_field() async {
@@ -1380,7 +1387,7 @@
c.x = i;
}
''');
- assertNullCheck(checkExpression('c.x'),
+ assertNullCheck(checkExpression('c.x')!,
assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true));
}
@@ -1393,7 +1400,7 @@
c..x = i;
}
''');
- assertNullCheck(checkExpression('c..x'),
+ assertNullCheck(checkExpression('c..x')!,
assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true));
}
@@ -1432,7 +1439,7 @@
c[i] = j;
}
''');
- assertNullCheck(checkExpression('c['),
+ assertNullCheck(checkExpression('c[')!,
assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true));
}
@@ -1489,7 +1496,7 @@
var yNullable = decoratedTypeAnnotation('int y').node;
var xNullable = decoratedTypeAnnotation('int x').node;
var returnNullable = decoratedTypeAnnotation('int f').node;
- var glbNode = decoratedExpressionType('(x ??= y)').node;
+ var glbNode = decoratedExpressionType('(x ??= y)')!.node;
assertEdge(yNullable, xNullable, hard: false, guards: [xNullable]);
assertEdge(yNullable, glbNode, hard: false, guards: [xNullable]);
assertEdge(glbNode, xNullable, hard: false);
@@ -1542,7 +1549,7 @@
int f(C c, int i) => (c?.s = i);
''');
var lubNode =
- decoratedExpressionType('(c?.s = i)').node as NullabilityNodeForLUB;
+ decoratedExpressionType('(c?.s = i)')!.node as NullabilityNodeForLUB;
expect(lubNode.left, same(decoratedTypeAnnotation('C c').node));
expect(lubNode.right, same(decoratedTypeAnnotation('int i').node));
assertEdge(lubNode, decoratedTypeAnnotation('int f').node, hard: false);
@@ -1557,7 +1564,7 @@
c.s = i;
}
''');
- assertNullCheck(checkExpression('c.s'),
+ assertNullCheck(checkExpression('c.s')!,
assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true));
}
@@ -1678,7 +1685,7 @@
var jNode = decoratedTypeAnnotation('int j').node;
assertEdge(iNode, jNode,
hard: false,
- guards: TypeMatcher<Iterable<NullabilityNode>>()
+ guards: TypeMatcher<Iterable<NullabilityNode?>>()
.having((g) => g.single, 'single value', isIn(alwaysPlus)));
}
@@ -1816,7 +1823,7 @@
int f(int i, int j) => i + j;
''');
- assertNullCheck(checkExpression('i +'),
+ assertNullCheck(checkExpression('i +')!,
assertEdge(decoratedTypeAnnotation('int i').node, never, hard: true));
}
@@ -1828,7 +1835,7 @@
Int f(Int i, Int j) => i + j;
''');
- assertNullCheck(checkExpression('i +'),
+ assertNullCheck(checkExpression('i +')!,
assertEdge(decoratedTypeAnnotation('Int i').node, never, hard: true));
}
@@ -1841,7 +1848,7 @@
''');
assertNullCheck(
- checkExpression('(i + j)'),
+ checkExpression('(i + j)')!,
assertEdge(decoratedTypeAnnotation('Int operator+').node,
decoratedTypeAnnotation('Int f').node,
hard: false));
@@ -1861,7 +1868,7 @@
''');
assertNullCheck(
- checkExpression('j;'),
+ checkExpression('j;')!,
assertEdge(decoratedTypeAnnotation('int j').node, inSet(pointsToNever),
hard: true));
}
@@ -1875,7 +1882,7 @@
''');
assertNullCheck(
- checkExpression('j/*check*/'),
+ checkExpression('j/*check*/')!,
assertEdge(decoratedTypeAnnotation('Int j').node,
decoratedTypeAnnotation('Int other').node,
hard: true));
@@ -1907,7 +1914,7 @@
var left = decoratedTypeAnnotation('int i').node;
var right = decoratedTypeAnnotation('int j').node;
- var expression = decoratedExpressionType('??').node;
+ var expression = decoratedExpressionType('??')!.node!;
assertEdge(right, expression, guards: [left], hard: false);
expect(expression.displayName, '?? operator (test.dart:1:24)');
}
@@ -1929,7 +1936,7 @@
}
C f(C x, dynamic y) => x + y;
''');
- assertNullCheck(checkExpression('x +'),
+ assertNullCheck(checkExpression('x +')!,
assertEdge(decoratedTypeAnnotation('C x').node, never, hard: true));
assertEdge(decoratedTypeAnnotation('C operator').node,
decoratedTypeAnnotation('C f').node,
@@ -2025,17 +2032,17 @@
D f(MyList<int>/*2*/ x) => D(x);
''');
var syntheticConstructor = findElement.unnamedConstructor('D');
- var constructorType = variables.decoratedElementType(syntheticConstructor);
- var constructorParameterType = constructorType.positionalParameters[0];
+ var constructorType = variables!.decoratedElementType(syntheticConstructor);
+ var constructorParameterType = constructorType.positionalParameters![0]!;
assertEdge(decoratedTypeAnnotation('MyList<int>/*2*/').node,
constructorParameterType.node,
hard: true);
assertEdge(decoratedTypeAnnotation('int>/*2*/').node,
- constructorParameterType.typeArguments[0].node,
+ constructorParameterType.typeArguments[0]!.node,
hard: true, checkable: false);
assertUnion(constructorParameterType.node,
decoratedTypeAnnotation('MyList<int>/*1*/').node);
- assertUnion(constructorParameterType.typeArguments[0].node,
+ assertUnion(constructorParameterType.typeArguments[0]!.node,
decoratedTypeAnnotation('int>/*1*/').node);
}
@@ -2049,8 +2056,8 @@
class D<U> = C<U> with M;
''');
var syntheticConstructor = findElement.unnamedConstructor('D');
- var constructorType = variables.decoratedElementType(syntheticConstructor);
- var constructorParameterType = constructorType.positionalParameters[0];
+ var constructorType = variables!.decoratedElementType(syntheticConstructor);
+ var constructorParameterType = constructorType.positionalParameters![0]!;
assertUnion(
constructorParameterType.node, decoratedTypeAnnotation('T t').node);
}
@@ -2066,8 +2073,8 @@
D f(int/*2*/ i) => D(i: i);
''');
var syntheticConstructor = findElement.unnamedConstructor('D');
- var constructorType = variables.decoratedElementType(syntheticConstructor);
- var constructorParameterType = constructorType.namedParameters['i'];
+ var constructorType = variables!.decoratedElementType(syntheticConstructor);
+ var constructorParameterType = constructorType.namedParameters!['i']!;
assertEdge(
decoratedTypeAnnotation('int/*2*/').node, constructorParameterType.node,
hard: true);
@@ -2086,8 +2093,8 @@
D f(int/*2*/ i) => D(i);
''');
var syntheticConstructor = findElement.unnamedConstructor('D');
- var constructorType = variables.decoratedElementType(syntheticConstructor);
- var constructorParameterType = constructorType.positionalParameters[0];
+ var constructorType = variables!.decoratedElementType(syntheticConstructor);
+ var constructorParameterType = constructorType.positionalParameters![0]!;
assertEdge(
decoratedTypeAnnotation('int/*2*/').node, constructorParameterType.node,
hard: true);
@@ -2106,8 +2113,8 @@
D f(int/*2*/ i) => D(i);
''');
var syntheticConstructor = findElement.unnamedConstructor('D');
- var constructorType = variables.decoratedElementType(syntheticConstructor);
- var constructorParameterType = constructorType.positionalParameters[0];
+ var constructorType = variables!.decoratedElementType(syntheticConstructor);
+ var constructorParameterType = constructorType.positionalParameters![0]!;
assertEdge(
decoratedTypeAnnotation('int/*2*/').node, constructorParameterType.node,
hard: true);
@@ -2132,7 +2139,7 @@
''');
var nullable_b = decoratedTypeAnnotation('bool b').node;
- var check_b = checkExpression('b ?');
+ var check_b = checkExpression('b ?')!;
assertNullCheck(check_b, assertEdge(nullable_b, never, hard: true));
}
@@ -2143,7 +2150,7 @@
decoratedTypeAnnotation('int y').node,
hard: false, guards: [guard]);
var conditionalDiscard =
- variables.conditionalDiscard(findNode.conditionalExpression('!='));
+ variables!.conditionalDiscard(findNode.conditionalExpression('!='))!;
expect(conditionalDiscard, isNotNull);
expect(conditionalDiscard.trueGuard, isNull);
expect(conditionalDiscard.falseGuard, same(guard));
@@ -2159,10 +2166,10 @@
decoratedGenericFunctionTypeAnnotation('void Function({int p}) x');
var yType =
decoratedGenericFunctionTypeAnnotation('void Function({int p}) y');
- var resultType = decoratedExpressionType('(b ?');
- assertLUB(resultType.node, xType.node, yType.node);
- assertGLB(resultType.namedParameters['p'].node,
- xType.namedParameters['p'].node, yType.namedParameters['p'].node);
+ var resultType = decoratedExpressionType('(b ?')!;
+ assertLUB(resultType.node!, xType.node, yType.node);
+ assertGLB(resultType.namedParameters!['p']!.node,
+ xType.namedParameters!['p']!.node, yType.namedParameters!['p']!.node);
}
Future<void>
@@ -2174,10 +2181,12 @@
''');
var xType = decoratedGenericFunctionTypeAnnotation('void Function(int) x');
var yType = decoratedGenericFunctionTypeAnnotation('void Function(int) y');
- var resultType = decoratedExpressionType('(b ?');
- assertLUB(resultType.node, xType.node, yType.node);
- assertGLB(resultType.positionalParameters[0].node,
- xType.positionalParameters[0].node, yType.positionalParameters[0].node);
+ var resultType = decoratedExpressionType('(b ?')!;
+ assertLUB(resultType.node!, xType.node, yType.node);
+ assertGLB(
+ resultType.positionalParameters![0]!.node,
+ xType.positionalParameters![0]!.node,
+ yType.positionalParameters![0]!.node);
}
Future<void>
@@ -2191,12 +2200,16 @@
decoratedGenericFunctionTypeAnnotation('void Function(int, int) x');
var yType =
decoratedGenericFunctionTypeAnnotation('void Function(int, int) y');
- var resultType = decoratedExpressionType('(b ?');
- assertLUB(resultType.node, xType.node, yType.node);
- assertGLB(resultType.positionalParameters[0].node,
- xType.positionalParameters[0].node, yType.positionalParameters[0].node);
- assertGLB(resultType.positionalParameters[1].node,
- xType.positionalParameters[1].node, yType.positionalParameters[1].node);
+ var resultType = decoratedExpressionType('(b ?')!;
+ assertLUB(resultType.node!, xType.node, yType.node);
+ assertGLB(
+ resultType.positionalParameters![0]!.node,
+ xType.positionalParameters![0]!.node,
+ yType.positionalParameters![0]!.node);
+ assertGLB(
+ resultType.positionalParameters![1]!.node,
+ xType.positionalParameters![1]!.node,
+ yType.positionalParameters![1]!.node);
}
Future<void>
@@ -2210,10 +2223,12 @@
decoratedGenericFunctionTypeAnnotation('void Function([int]) x');
var yType =
decoratedGenericFunctionTypeAnnotation('void Function([int]) y');
- var resultType = decoratedExpressionType('(b ?');
- assertLUB(resultType.node, xType.node, yType.node);
- assertGLB(resultType.positionalParameters[0].node,
- xType.positionalParameters[0].node, yType.positionalParameters[0].node);
+ var resultType = decoratedExpressionType('(b ?')!;
+ assertLUB(resultType.node!, xType.node, yType.node);
+ assertGLB(
+ resultType.positionalParameters![0]!.node,
+ xType.positionalParameters![0]!.node,
+ yType.positionalParameters![0]!.node);
}
Future<void> test_conditionalExpression_functionTyped_returnType() async {
@@ -2224,10 +2239,10 @@
''');
var xType = decoratedGenericFunctionTypeAnnotation('int Function() x');
var yType = decoratedGenericFunctionTypeAnnotation('int Function() y');
- var resultType = decoratedExpressionType('(b ?');
- assertLUB(resultType.node, xType.node, yType.node);
- assertLUB(resultType.returnType.node, xType.returnType.node,
- yType.returnType.node);
+ var resultType = decoratedExpressionType('(b ?')!;
+ assertLUB(resultType.node!, xType.node, yType.node);
+ assertLUB(resultType.returnType!.node!, xType.returnType!.node,
+ yType.returnType!.node);
}
Future<void>
@@ -2239,9 +2254,9 @@
''');
var xType = decoratedGenericFunctionTypeAnnotation('void Function() x');
var yType = decoratedGenericFunctionTypeAnnotation('void Function() y');
- var resultType = decoratedExpressionType('(b ?');
- assertLUB(resultType.node, xType.node, yType.node);
- expect(resultType.returnType.node.isImmutable, false);
+ var resultType = decoratedExpressionType('(b ?')!;
+ assertLUB(resultType.node!, xType.node, yType.node);
+ expect(resultType.returnType!.node!.isImmutable, false);
}
Future<void> test_conditionalExpression_general() async {
@@ -2253,10 +2268,10 @@
var nullable_i = decoratedTypeAnnotation('int i').node;
var nullable_j = decoratedTypeAnnotation('int j').node;
- var nullable_conditional = decoratedExpressionType('(b ?').node;
+ var nullable_conditional = decoratedExpressionType('(b ?')!.node!;
assertLUB(nullable_conditional, nullable_i, nullable_j);
var nullable_return = decoratedTypeAnnotation('int f').node;
- assertNullCheck(checkExpression('(b ? i : j)'),
+ assertNullCheck(checkExpression('(b ? i : j)')!,
assertEdge(nullable_conditional, nullable_return, hard: false));
}
@@ -2268,12 +2283,12 @@
''');
var xType = decoratedTypeAnnotation('Map<int, String> x');
var yType = decoratedTypeAnnotation('Map<int, String> y');
- var resultType = decoratedExpressionType('(b ?');
- assertLUB(resultType.node, xType.node, yType.node);
- assertLUB(resultType.typeArguments[0].node, xType.typeArguments[0].node,
- yType.typeArguments[0].node);
- assertLUB(resultType.typeArguments[1].node, xType.typeArguments[1].node,
- yType.typeArguments[1].node);
+ var resultType = decoratedExpressionType('(b ?')!;
+ assertLUB(resultType.node!, xType.node, yType.node);
+ assertLUB(resultType.typeArguments[0]!.node!, xType.typeArguments[0]!.node,
+ yType.typeArguments[0]!.node);
+ assertLUB(resultType.typeArguments[1]!.node!, xType.typeArguments[1]!.node,
+ yType.typeArguments[1]!.node);
}
Future<void> test_conditionalExpression_generic_lub() async {
@@ -2289,12 +2304,12 @@
var cType = decoratedTypeAnnotation('C<num> y');
var bInA = decoratedTypeAnnotation('T/*b*/');
var cInA = decoratedTypeAnnotation('T/*c*/');
- var resultType = decoratedExpressionType('(b ?');
- assertLUB(resultType.node, bType.node, cType.node);
+ var resultType = decoratedExpressionType('(b ?')!;
+ assertLUB(resultType.node!, bType.node, cType.node);
assertLUB(
- resultType.typeArguments[0].node,
- substitutionNode(bType.typeArguments[0].node, bInA.node),
- substitutionNode(cType.typeArguments[0].node, cInA.node));
+ resultType.typeArguments[0]!.node!,
+ substitutionNode(bType.typeArguments[0]!.node, bInA.node),
+ substitutionNode(cType.typeArguments[0]!.node, cInA.node));
}
Future<void> test_conditionalExpression_generic_lub_leftSubtype() async {
@@ -2308,12 +2323,12 @@
var aType = decoratedTypeAnnotation('A<num> y');
var bType = decoratedTypeAnnotation('B<num> x');
var bInA = decoratedTypeAnnotation('T/*b*/');
- var resultType = decoratedExpressionType('(b ?');
- assertLUB(resultType.node, bType.node, aType.node);
+ var resultType = decoratedExpressionType('(b ?')!;
+ assertLUB(resultType.node!, bType.node, aType.node);
assertLUB(
- resultType.typeArguments[0].node,
- substitutionNode(bType.typeArguments[0].node, bInA.node),
- aType.typeArguments[0].node);
+ resultType.typeArguments[0]!.node!,
+ substitutionNode(bType.typeArguments[0]!.node, bInA.node),
+ aType.typeArguments[0]!.node);
}
Future<void> test_conditionalExpression_generic_lub_rightSubtype() async {
@@ -2327,10 +2342,10 @@
var aType = decoratedTypeAnnotation('A<num> x');
var bType = decoratedTypeAnnotation('B<num> y');
var bInA = decoratedTypeAnnotation('T/*b*/');
- var resultType = decoratedExpressionType('(b ?');
- assertLUB(resultType.node, aType.node, bType.node);
- assertLUB(resultType.typeArguments[0].node, aType.typeArguments[0].node,
- substitutionNode(bType.typeArguments[0].node, bInA.node));
+ var resultType = decoratedExpressionType('(b ?')!;
+ assertLUB(resultType.node!, aType.node, bType.node);
+ assertLUB(resultType.typeArguments[0]!.node!, aType.typeArguments[0]!.node,
+ substitutionNode(bType.typeArguments[0]!.node, bInA.node));
}
Future<void> test_conditionalExpression_generic_typeParameter_bound() async {
@@ -2342,11 +2357,11 @@
var aType = decoratedTypeAnnotation('List<num> x');
var bType = decoratedTypeAnnotation('T y');
var bBound = decoratedTypeAnnotation('List<num>>');
- var resultType = decoratedExpressionType('(b ?');
- assertLUB(
- resultType.node, aType.node, substitutionNode(bBound.node, bType.node));
- assertLUB(resultType.typeArguments[0].node, aType.typeArguments[0].node,
- bBound.typeArguments[0].node);
+ var resultType = decoratedExpressionType('(b ?')!;
+ assertLUB(resultType.node!, aType.node,
+ substitutionNode(bBound.node, bType.node));
+ assertLUB(resultType.typeArguments[0]!.node!, aType.typeArguments[0]!.node,
+ bBound.typeArguments[0]!.node);
}
Future<void> test_conditionalExpression_left_never() async {
@@ -2358,7 +2373,7 @@
var nullable_i = decoratedTypeAnnotation('List<int> i').node;
var nullable_conditional =
- decoratedExpressionType('(b ?').node as NullabilityNodeForLUB;
+ decoratedExpressionType('(b ?')!.node as NullabilityNodeForLUB;
var nullable_throw = nullable_conditional.left;
assertNoUpstreamNullability(nullable_throw);
assertLUB(nullable_conditional, nullable_throw, nullable_i);
@@ -2373,7 +2388,7 @@
var nullable_i = decoratedTypeAnnotation('int i').node;
var nullable_conditional =
- decoratedExpressionType('(b ?').node as NullabilityNodeForLUB;
+ decoratedExpressionType('(b ?')!.node as NullabilityNodeForLUB;
var nullable_throw = nullable_conditional.left;
assertNoUpstreamNullability(nullable_throw);
assertLUB(nullable_conditional, nullable_throw, nullable_i);
@@ -2387,7 +2402,7 @@
''');
var nullable_i = decoratedTypeAnnotation('int i').node;
- var nullable_conditional = decoratedExpressionType('(b ?').node;
+ var nullable_conditional = decoratedExpressionType('(b ?')!.node!;
assertLUB(nullable_conditional, inSet(alwaysPlus), nullable_i);
}
@@ -2400,7 +2415,7 @@
var nullable_i =
decoratedGenericFunctionTypeAnnotation('bool Function<T>(int) f').node;
- var nullable_conditional = decoratedExpressionType('(b ?').node;
+ var nullable_conditional = decoratedExpressionType('(b ?')!.node!;
assertLUB(nullable_conditional, inSet(alwaysPlus), nullable_i);
}
@@ -2413,7 +2428,7 @@
''');
var nullable_t = decoratedTypeAnnotation('T t').node;
- var nullable_conditional = decoratedExpressionType('(b ?').node;
+ var nullable_conditional = decoratedExpressionType('(b ?')!.node!;
assertLUB(nullable_conditional, inSet(alwaysPlus), nullable_t);
}
@@ -2425,7 +2440,7 @@
''');
var nullable_i = decoratedTypeAnnotation('List<int> l').node;
- var nullable_conditional = decoratedExpressionType('(b ?').node;
+ var nullable_conditional = decoratedExpressionType('(b ?')!.node!;
assertLUB(nullable_conditional, inSet(alwaysPlus), nullable_i);
}
@@ -2439,10 +2454,12 @@
decoratedGenericFunctionTypeAnnotation('void Function(Null p) x');
var yType =
decoratedGenericFunctionTypeAnnotation('void Function(List<int> p) y');
- var resultType = decoratedExpressionType('(b ?');
- assertLUB(resultType.node, xType.node, yType.node);
- assertGLB(resultType.positionalParameters[0].node,
- xType.positionalParameters[0].node, yType.positionalParameters[0].node);
+ var resultType = decoratedExpressionType('(b ?')!;
+ assertLUB(resultType.node!, xType.node, yType.node);
+ assertGLB(
+ resultType.positionalParameters![0]!.node,
+ xType.positionalParameters![0]!.node,
+ yType.positionalParameters![0]!.node);
}
Future<void> test_conditionalExpression_parameterType() async {
@@ -2454,7 +2471,7 @@
var nullable_x = decoratedTypeAnnotation('T x').node;
var nullable_y = decoratedTypeAnnotation('T y').node;
- var nullable_conditional = decoratedExpressionType('(b ?').node;
+ var nullable_conditional = decoratedExpressionType('(b ?')!.node!;
assertLUB(nullable_conditional, nullable_x, nullable_y);
}
@@ -2467,7 +2484,7 @@
var nullable_i = decoratedTypeAnnotation('List<int> i').node;
var nullable_conditional =
- decoratedExpressionType('(b ?').node as NullabilityNodeForLUB;
+ decoratedExpressionType('(b ?')!.node as NullabilityNodeForLUB;
var nullable_throw = nullable_conditional.right;
assertNoUpstreamNullability(nullable_throw);
assertLUB(nullable_conditional, nullable_i, nullable_throw);
@@ -2482,7 +2499,7 @@
var nullable_i = decoratedTypeAnnotation('int i').node;
var nullable_conditional =
- decoratedExpressionType('(b ?').node as NullabilityNodeForLUB;
+ decoratedExpressionType('(b ?')!.node as NullabilityNodeForLUB;
var nullable_throw = nullable_conditional.right;
assertNoUpstreamNullability(nullable_throw);
assertLUB(nullable_conditional, nullable_i, nullable_throw);
@@ -2496,7 +2513,7 @@
''');
var nullable_i = decoratedTypeAnnotation('int i').node;
- var nullable_conditional = decoratedExpressionType('(b ?').node;
+ var nullable_conditional = decoratedExpressionType('(b ?')!.node!;
assertLUB(nullable_conditional, nullable_i, inSet(alwaysPlus));
}
@@ -2509,7 +2526,7 @@
var nullable_i =
decoratedGenericFunctionTypeAnnotation('bool Function<T>(int) f').node;
- var nullable_conditional = decoratedExpressionType('(b ?').node;
+ var nullable_conditional = decoratedExpressionType('(b ?')!.node!;
assertLUB(nullable_conditional, nullable_i, inSet(alwaysPlus));
}
@@ -2521,7 +2538,7 @@
''');
var nullable_i = decoratedTypeAnnotation('List<int> l').node;
- var nullable_conditional = decoratedExpressionType('(b ?').node;
+ var nullable_conditional = decoratedExpressionType('(b ?')!.node!;
assertLUB(nullable_conditional, nullable_i, inSet(alwaysPlus));
}
@@ -2534,7 +2551,7 @@
''');
var nullable_t = decoratedTypeAnnotation('T t').node;
- var nullable_conditional = decoratedExpressionType('(b ?').node;
+ var nullable_conditional = decoratedExpressionType('(b ?')!.node!;
assertLUB(nullable_conditional, nullable_t, inSet(alwaysPlus));
}
@@ -2545,7 +2562,7 @@
decoratedTypeAnnotation('int y').node,
hard: false, guards: [guard]);
var conditionalDiscard =
- variables.conditionalDiscard(findNode.conditionalExpression('=='));
+ variables!.conditionalDiscard(findNode.conditionalExpression('=='))!;
expect(conditionalDiscard, isNotNull);
expect(conditionalDiscard.trueGuard, same(guard));
expect(conditionalDiscard.falseGuard, isNull);
@@ -2560,9 +2577,9 @@
var aType = decoratedTypeAnnotation('num x');
var bType = decoratedTypeAnnotation('T y');
var bBound = decoratedTypeAnnotation('num>');
- var resultType = decoratedExpressionType('(b ?');
- assertLUB(
- resultType.node, aType.node, substitutionNode(bBound.node, bType.node));
+ var resultType = decoratedExpressionType('(b ?')!;
+ assertLUB(resultType.node!, aType.node,
+ substitutionNode(bBound.node, bType.node));
}
Future<void> test_conditionalExpression_typeParameter_bound_bound() async {
@@ -2575,9 +2592,9 @@
var bType = decoratedTypeAnnotation('T y');
var bBound = decoratedTypeAnnotation('R,');
var bBoundBound = decoratedTypeAnnotation('num>');
- var resultType = decoratedExpressionType('(b ?');
+ var resultType = decoratedExpressionType('(b ?')!;
assertLUB(
- resultType.node,
+ resultType.node!,
aType.node,
substitutionNode(
bBoundBound.node, substitutionNode(bBound.node, bType.node)));
@@ -2593,9 +2610,9 @@
var aType = decoratedTypeAnnotation('dynamic x');
var bType = decoratedTypeAnnotation('T y');
var bBound = decoratedTypeAnnotation('num>');
- var resultType = decoratedExpressionType('(b ?');
- assertLUB(
- resultType.node, aType.node, substitutionNode(bBound.node, bType.node));
+ var resultType = decoratedExpressionType('(b ?')!;
+ assertLUB(resultType.node!, aType.node,
+ substitutionNode(bBound.node, bType.node));
}
Future<void> test_conditionalExpression_typeParameters_bound() async {
@@ -2608,8 +2625,8 @@
var bType = decoratedTypeAnnotation('T y');
var aBound = decoratedTypeAnnotation('num>');
var bBound = decoratedTypeAnnotation('num,');
- var resultType = decoratedExpressionType('(b ?');
- assertLUB(resultType.node, substitutionNode(aBound.node, aType.node),
+ var resultType = decoratedExpressionType('(b ?')!;
+ assertLUB(resultType.node!, substitutionNode(aBound.node, aType.node),
substitutionNode(bBound.node, bType.node));
}
@@ -2623,9 +2640,9 @@
var aType = decoratedTypeAnnotation('R x');
var bType = decoratedTypeAnnotation('T y');
var bBound = decoratedTypeAnnotation('R,');
- var resultType = decoratedExpressionType('(b ?');
- assertLUB(
- resultType.node, aType.node, substitutionNode(bBound.node, bType.node));
+ var resultType = decoratedExpressionType('(b ?')!;
+ assertLUB(resultType.node!, aType.node,
+ substitutionNode(bBound.node, bType.node));
}
Future<void> test_constructor_default_parameter_value_bool() async {
@@ -2658,8 +2675,8 @@
''');
var namedConstructor = findElement.constructor('named', of: 'C');
- var constructorType = variables.decoratedElementType(namedConstructor);
- var constructorParameterType = constructorType.positionalParameters[0];
+ var constructorType = variables!.decoratedElementType(namedConstructor);
+ var constructorParameterType = constructorType.positionalParameters![0]!;
assertEdge(
decoratedTypeAnnotation('int j').node, constructorParameterType.node,
hard: true);
@@ -2706,18 +2723,18 @@
}
''');
var constructor = findElement.unnamedConstructor('C');
- var constructorDecoratedType = variables.decoratedElementType(constructor);
- _assertType(constructorDecoratedType.type, 'C<T, U> Function()');
+ var constructorDecoratedType = variables!.decoratedElementType(constructor);
+ _assertType(constructorDecoratedType.type!, 'C<T, U> Function()');
expect(constructorDecoratedType.node, same(never));
expect(constructorDecoratedType.typeFormals, isEmpty);
- expect(constructorDecoratedType.returnType.node, same(never));
- _assertType(constructorDecoratedType.returnType.type, 'C<T, U>');
- var typeArguments = constructorDecoratedType.returnType.typeArguments;
+ expect(constructorDecoratedType.returnType!.node, same(never));
+ _assertType(constructorDecoratedType.returnType!.type!, 'C<T, U>');
+ var typeArguments = constructorDecoratedType.returnType!.typeArguments;
expect(typeArguments, hasLength(2));
- _assertType(typeArguments[0].type, 'T');
- expect(typeArguments[0].node, same(never));
- _assertType(typeArguments[1].type, 'U');
- expect(typeArguments[1].node, same(never));
+ _assertType(typeArguments[0]!.type!, 'T');
+ expect(typeArguments[0]!.node, same(never));
+ _assertType(typeArguments[1]!.type!, 'U');
+ expect(typeArguments[1]!.node, same(never));
}
Future<void> test_constructorDeclaration_returnType_generic_implicit() async {
@@ -2725,18 +2742,18 @@
class C<T, U> {}
''');
var constructor = findElement.unnamedConstructor('C');
- var constructorDecoratedType = variables.decoratedElementType(constructor);
- _assertType(constructorDecoratedType.type, 'C<T, U> Function()');
+ var constructorDecoratedType = variables!.decoratedElementType(constructor);
+ _assertType(constructorDecoratedType.type!, 'C<T, U> Function()');
expect(constructorDecoratedType.node, same(never));
expect(constructorDecoratedType.typeFormals, isEmpty);
- expect(constructorDecoratedType.returnType.node, same(never));
- _assertType(constructorDecoratedType.returnType.type, 'C<T, U>');
- var typeArguments = constructorDecoratedType.returnType.typeArguments;
+ expect(constructorDecoratedType.returnType!.node, same(never));
+ _assertType(constructorDecoratedType.returnType!.type!, 'C<T, U>');
+ var typeArguments = constructorDecoratedType.returnType!.typeArguments;
expect(typeArguments, hasLength(2));
- _assertType(typeArguments[0].type, 'T');
- expect(typeArguments[0].node, same(never));
- _assertType(typeArguments[1].type, 'U');
- expect(typeArguments[1].node, same(never));
+ _assertType(typeArguments[0]!.type!, 'T');
+ expect(typeArguments[0]!.node, same(never));
+ _assertType(typeArguments[1]!.type!, 'U');
+ expect(typeArguments[1]!.node, same(never));
}
Future<void> test_constructorDeclaration_returnType_simple() async {
@@ -2746,12 +2763,12 @@
}
''');
var constructorDecoratedType =
- variables.decoratedElementType(findElement.unnamedConstructor('C'));
- _assertType(constructorDecoratedType.type, 'C Function()');
+ variables!.decoratedElementType(findElement.unnamedConstructor('C'));
+ _assertType(constructorDecoratedType.type!, 'C Function()');
expect(constructorDecoratedType.node, same(never));
expect(constructorDecoratedType.typeFormals, isEmpty);
- expect(constructorDecoratedType.returnType.node, same(never));
- expect(constructorDecoratedType.returnType.typeArguments, isEmpty);
+ expect(constructorDecoratedType.returnType!.node, same(never));
+ expect(constructorDecoratedType.returnType!.typeArguments, isEmpty);
}
Future<void> test_constructorDeclaration_returnType_simple_implicit() async {
@@ -2759,12 +2776,12 @@
class C {}
''');
var constructorDecoratedType =
- variables.decoratedElementType(findElement.unnamedConstructor('C'));
- _assertType(constructorDecoratedType.type, 'C Function()');
+ variables!.decoratedElementType(findElement.unnamedConstructor('C'));
+ _assertType(constructorDecoratedType.type!, 'C Function()');
expect(constructorDecoratedType.node, same(never));
expect(constructorDecoratedType.typeFormals, isEmpty);
- expect(constructorDecoratedType.returnType.node, same(never));
- expect(constructorDecoratedType.returnType.typeArguments, isEmpty);
+ expect(constructorDecoratedType.returnType!.node, same(never));
+ expect(constructorDecoratedType.returnType!.typeArguments, isEmpty);
}
Future<void> test_constructorFieldInitializer_generic() async {
@@ -2810,7 +2827,7 @@
}
''');
- assertNullCheck(checkExpression('b);'),
+ assertNullCheck(checkExpression('b);')!,
assertEdge(decoratedTypeAnnotation('bool b').node, never, hard: true));
}
@@ -3153,7 +3170,7 @@
}
''');
var xType =
- variables.decoratedElementType(findNode.simple('x').staticElement);
+ variables!.decoratedElementType(findNode.simple('x').staticElement!);
assertEdge(decoratedTypeAnnotation('int').node, xType.node, hard: false);
}
@@ -3164,18 +3181,18 @@
C(int this.f(int i, {int j}));
}
''');
- var ctorParamType = variables
+ var ctorParamType = variables!
.decoratedElementType(findElement.unnamedConstructor('C'))
- .positionalParameters[0];
- var fieldType = variables.decoratedElementType(findElement.field('f'));
+ .positionalParameters![0]!;
+ var fieldType = variables!.decoratedElementType(findElement.field('f'));
assertEdge(ctorParamType.node, fieldType.node, hard: true);
- assertEdge(ctorParamType.returnType.node, fieldType.returnType.node,
+ assertEdge(ctorParamType.returnType!.node, fieldType.returnType!.node,
hard: false, checkable: false);
- assertEdge(fieldType.positionalParameters[0].node,
- ctorParamType.positionalParameters[0].node,
+ assertEdge(fieldType.positionalParameters![0]!.node,
+ ctorParamType.positionalParameters![0]!.node,
hard: false, checkable: false);
- assertEdge(fieldType.namedParameters['j'].node,
- ctorParamType.namedParameters['j'].node,
+ assertEdge(fieldType.namedParameters!['j']!.node,
+ ctorParamType.namedParameters!['j']!.node,
hard: false, checkable: false);
}
@@ -3199,7 +3216,7 @@
}
''');
var decoratedConstructorParamType =
- decoratedConstructorDeclaration('named').positionalParameters[0];
+ decoratedConstructorDeclaration('named').positionalParameters![0]!;
assertEdge(decoratedConstructorParamType.node,
decoratedTypeAnnotation('int i').node,
hard: true);
@@ -3218,23 +3235,23 @@
// `firstWhereOrNull` in order to avoid having to make `x`'s type argument
// nullable, we need a synthetic edge to ensure that the return type of
// `firstEven` is nullable.
- var closureReturnType = decoratedExpressionType('() => null').returnType;
- var firstWhereReturnType = variables
- .decoratedExpressionType(findNode.methodInvocation('firstWhere'));
+ var closureReturnType = decoratedExpressionType('() => null')!.returnType!;
+ var firstWhereReturnType = variables!
+ .decoratedExpressionType(findNode.methodInvocation('firstWhere'))!;
assertEdge(closureReturnType.node, firstWhereReturnType.node, hard: false);
// There should also be an edge from a substitution node to the return type
// of `firstWhere`, to account for the normal data flow (when the element is
// found).
var typeParameterType = decoratedTypeAnnotation('int>');
- var firstWhereType = variables.decoratedElementType(findNode
+ var firstWhereType = variables!.decoratedElementType(findNode
.methodInvocation('firstWhere')
.methodName
- .staticElement
- .declaration);
+ .staticElement!
+ .declaration!);
assertEdge(
substitutionNode(
- typeParameterType.node, firstWhereType.returnType.node),
+ typeParameterType.node, firstWhereType.returnType!.node),
firstWhereReturnType.node,
hard: false);
}
@@ -3377,7 +3394,7 @@
''');
assertNullCheck(
- checkExpression('ints) i'),
+ checkExpression('ints) i')!,
assertEdge(decoratedTypeAnnotation('List<int> ints').node, never,
hard: true));
assertEdge(decoratedTypeAnnotation('int i').node,
@@ -3397,11 +3414,11 @@
''');
assertNullCheck(
- checkExpression('strs)\n'),
+ checkExpression('strs)\n')!,
assertEdge(decoratedTypeAnnotation('List<String> strs').node, never,
hard: true));
assertNullCheck(
- checkExpression('ints)\n'),
+ checkExpression('ints)\n')!,
assertEdge(decoratedTypeAnnotation('List<int> ints').node, never,
hard: false));
@@ -3421,7 +3438,7 @@
''');
assertNullCheck(
- checkExpression('ints) i'),
+ checkExpression('ints) i')!,
assertEdge(decoratedTypeAnnotation('List<int> ints').node, never,
hard: true));
assertEdge(decoratedTypeAnnotation('int i').node,
@@ -3492,7 +3509,7 @@
''');
assertNullCheck(
- checkExpression('i/*3*/'),
+ checkExpression('i/*3*/')!,
assertEdge(decoratedTypeAnnotation('int/*2*/').node,
decoratedTypeAnnotation('int/*1*/').node,
hard: true));
@@ -3634,7 +3651,7 @@
var int_1 = decoratedTypeAnnotation('int/*1*/');
var int_2 = decoratedTypeAnnotation('int/*2*/');
- var i_3 = checkExpression('i/*3*/');
+ var i_3 = checkExpression('i/*3*/')!;
assertNullCheck(i_3, assertEdge(int_2.node, int_1.node, hard: true));
assertEdge(int_2.node, int_1.node, hard: true);
}
@@ -3647,9 +3664,9 @@
}
''');
- var parameter = variables.decoratedElementType(
- findNode.functionTypedFormalParameter('void g()').declaredElement);
- assertNullCheck(checkExpression('null'),
+ var parameter = variables!.decoratedElementType(
+ findNode.functionTypedFormalParameter('void g()').declaredElement!);
+ assertNullCheck(checkExpression('null')!,
assertEdge(inSet(alwaysPlus), parameter.node, hard: false));
}
@@ -3661,8 +3678,8 @@
f();
}
''');
- var parameter = variables.decoratedElementType(
- findNode.functionTypedFormalParameter('void g()').declaredElement);
+ var parameter = variables!.decoratedElementType(
+ findNode.functionTypedFormalParameter('void g()').declaredElement!);
expect(getEdges(always, parameter.node), isNotEmpty);
}
@@ -3675,7 +3692,7 @@
''');
var nullable_i = decoratedTypeAnnotation('int i').node;
var nullable_j = decoratedTypeAnnotation('int j').node;
- assertNullCheck(checkExpression('j/*check*/'),
+ assertNullCheck(checkExpression('j/*check*/')!,
assertEdge(nullable_j, nullable_i, hard: true));
}
@@ -3729,7 +3746,7 @@
''');
assertNullCheck(
- checkExpression('null'),
+ checkExpression('null')!,
assertEdge(inSet(alwaysPlus), decoratedTypeAnnotation('int').node,
hard: false));
}
@@ -3743,7 +3760,7 @@
''');
assertNullCheck(
- checkExpression('(f())'),
+ checkExpression('(f())')!,
assertEdge(decoratedTypeAnnotation('int/*1*/').node,
decoratedTypeAnnotation('int/*2*/').node,
hard: false));
@@ -3899,7 +3916,7 @@
}
''');
- assertNullCheck(checkExpression('b) {}'),
+ assertNullCheck(checkExpression('b) {}')!,
assertEdge(decoratedTypeAnnotation('bool b').node, never, hard: true));
}
@@ -3965,12 +3982,12 @@
var nullable_k = decoratedTypeAnnotation('int k').node;
var nullable_itemType = decoratedTypeAnnotation('int>[').node;
assertNullCheck(
- checkExpression('j/*check*/'),
+ checkExpression('j/*check*/')!,
assertEdge(nullable_j, nullable_itemType,
guards: [nullable_i], hard: false));
- assertNullCheck(checkExpression('k/*check*/'),
+ assertNullCheck(checkExpression('k/*check*/')!,
assertEdge(nullable_k, nullable_itemType, hard: false));
- var discard = elementDiscard('if (i == null)');
+ var discard = elementDiscard('if (i == null)')!;
expect(discard.trueGuard, same(nullable_i));
expect(discard.falseGuard, null);
expect(discard.pureCondition, true);
@@ -3985,7 +4002,7 @@
}
''');
- assertNullCheck(checkExpression('b) i1'),
+ assertNullCheck(checkExpression('b) i1')!,
assertEdge(decoratedTypeAnnotation('bool b').node, never, hard: true));
assertEdge(decoratedTypeAnnotation('int i1').node,
decoratedTypeAnnotation('int>[').node,
@@ -4006,7 +4023,7 @@
}
''');
- assertNullCheck(checkExpression('b) s1'),
+ assertNullCheck(checkExpression('b) s1')!,
assertEdge(decoratedTypeAnnotation('bool b').node, never, hard: true));
var keyTypeNode = decoratedTypeAnnotation('String, int>{').node;
@@ -4031,10 +4048,10 @@
}
''');
- assertNullCheck(checkExpression('b1)'),
+ assertNullCheck(checkExpression('b1)')!,
assertEdge(decoratedTypeAnnotation('bool b1').node, never, hard: true));
assertNullCheck(
- checkExpression('b2) i1'),
+ checkExpression('b2) i1')!,
assertEdge(decoratedTypeAnnotation('bool b2').node, never,
hard: false));
assertEdge(decoratedTypeAnnotation('int i1').node,
@@ -4057,7 +4074,7 @@
}
''');
- assertNullCheck(checkExpression('b) i1'),
+ assertNullCheck(checkExpression('b) i1')!,
assertEdge(decoratedTypeAnnotation('bool b').node, never, hard: true));
assertEdge(decoratedTypeAnnotation('int i1').node,
decoratedTypeAnnotation('int>{').node,
@@ -4082,12 +4099,12 @@
var nullable_k = decoratedTypeAnnotation('int k').node;
var nullable_return = decoratedTypeAnnotation('int f').node;
assertNullCheck(
- checkExpression('j/*check*/'),
+ checkExpression('j/*check*/')!,
assertEdge(nullable_j, nullable_return,
guards: [nullable_i], hard: false));
- assertNullCheck(checkExpression('k/*check*/'),
+ assertNullCheck(checkExpression('k/*check*/')!,
assertEdge(nullable_k, nullable_return, hard: false));
- var discard = statementDiscard('if (i == null)');
+ var discard = statementDiscard('if (i == null)')!;
expect(discard.trueGuard, same(nullable_i));
expect(discard.falseGuard, null);
expect(discard.pureCondition, true);
@@ -4107,9 +4124,9 @@
var nullable_i = decoratedTypeAnnotation('int i').node;
var nullable_j = decoratedTypeAnnotation('int j').node;
var nullable_return = decoratedTypeAnnotation('int f').node;
- assertNullCheck(checkExpression('i/*check*/'),
+ assertNullCheck(checkExpression('i/*check*/')!,
assertEdge(nullable_i, nullable_return, hard: false));
- assertNullCheck(checkExpression('j/*check*/'),
+ assertNullCheck(checkExpression('j/*check*/')!,
assertEdge(nullable_j, nullable_return, hard: false));
}
@@ -4125,7 +4142,7 @@
var nullable_i = decoratedTypeAnnotation('int i').node;
var nullable_return = decoratedTypeAnnotation('int f').node;
- assertNullCheck(checkExpression('i/*check*/'),
+ assertNullCheck(checkExpression('i/*check*/')!,
assertEdge(nullable_i, nullable_return, hard: false));
}
@@ -4209,7 +4226,7 @@
}
int f(C c) => c[0];
''');
- assertNullCheck(checkExpression('c['),
+ assertNullCheck(checkExpression('c[')!,
assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true));
}
@@ -4220,7 +4237,7 @@
}
C f(C c) => c..[0];
''');
- assertNullCheck(checkExpression('c..['),
+ assertNullCheck(checkExpression('c..[')!,
assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true));
}
@@ -4305,10 +4322,10 @@
f(int i) => C<int>(i/*check*/);
''');
var nullable_i = decoratedTypeAnnotation('int i').node;
- var nullable_c_t = decoratedTypeAnnotation('C<int>').typeArguments[0].node;
+ var nullable_c_t = decoratedTypeAnnotation('C<int>').typeArguments[0]!.node;
var nullable_t = decoratedTypeAnnotation('T t').node;
- var check_i = checkExpression('i/*check*/');
- var nullable_c_t_or_nullable_t = check_i.checks.edges[FixReasonTarget.root]
+ var check_i = checkExpression('i/*check*/')!;
+ var nullable_c_t_or_nullable_t = check_i.checks.edges[FixReasonTarget.root]!
.destinationNode as NullabilityNodeForSubstitution;
expect(nullable_c_t_or_nullable_t.innerNode, same(nullable_c_t));
expect(nullable_c_t_or_nullable_t.outerNode, same(nullable_t));
@@ -4324,10 +4341,10 @@
f(int i) => C<int>(t: i/*check*/);
''');
var nullable_i = decoratedTypeAnnotation('int i').node;
- var nullable_c_t = decoratedTypeAnnotation('C<int>').typeArguments[0].node;
+ var nullable_c_t = decoratedTypeAnnotation('C<int>').typeArguments[0]!.node;
var nullable_t = decoratedTypeAnnotation('T t').node;
- var check_i = checkExpression('i/*check*/');
- var nullable_c_t_or_nullable_t = check_i.checks.edges[FixReasonTarget.root]
+ var check_i = checkExpression('i/*check*/')!;
+ var nullable_c_t_or_nullable_t = check_i.checks.edges[FixReasonTarget.root]!
.destinationNode as NullabilityNodeForSubstitution;
expect(nullable_c_t_or_nullable_t.innerNode, same(nullable_c_t));
expect(nullable_c_t_or_nullable_t.outerNode, same(nullable_t));
@@ -4345,14 +4362,14 @@
var edge0 = assertEdge(
anyNode, decoratedTypeAnnotation('C<Object, Object>').node,
hard: false);
- expect(edge0.sourceNode.displayName, 'constructed type (test.dart:3:25)');
+ expect(edge0.sourceNode!.displayName, 'constructed type (test.dart:3:25)');
var edge1 = assertEdge(anyNode, decoratedTypeAnnotation('Object,').node,
hard: false, checkable: false);
- expect(edge1.sourceNode.displayName,
+ expect(edge1.sourceNode!.displayName,
'type argument 0 of constructed type (test.dart:3:25)');
var edge2 = assertEdge(anyNode, decoratedTypeAnnotation('Object>').node,
hard: false, checkable: false);
- expect(edge2.sourceNode.displayName,
+ expect(edge2.sourceNode!.displayName,
'type argument 1 of constructed type (test.dart:3:25)');
}
@@ -4461,7 +4478,7 @@
assertEdge(inSet(alwaysPlus), decoratedTypeAnnotation('int f').node,
hard: false);
assertNullCheck(
- checkExpression('g('),
+ checkExpression('g(')!,
assertEdge(decoratedTypeAnnotation('Function g').node, never,
hard: true));
}
@@ -4473,7 +4490,7 @@
assertEdge(inSet(alwaysPlus), decoratedTypeAnnotation('int f').node,
hard: false);
assertNullCheck(
- checkExpression('g)('),
+ checkExpression('g)(')!,
assertEdge(decoratedTypeAnnotation('Function g').node, never,
hard: true));
}
@@ -4603,7 +4620,7 @@
expect(returnTypeEdges.length, 1);
final returnTypeEdge = returnTypeEdges.single;
- final listArgType = returnTypeEdge.sourceNode;
+ final listArgType = returnTypeEdge.sourceNode!;
assertNoUpstreamNullability(listArgType);
expect(listArgType.displayName, 'list element type (test.dart:2:10)');
}
@@ -4658,7 +4675,7 @@
}
''');
var xType =
- variables.decoratedElementType(findNode.simple('x').staticElement);
+ variables!.decoratedElementType(findNode.simple('x').staticElement!);
assertEdge(decoratedTypeAnnotation('int').node, xType.node, hard: false);
}
@@ -4682,8 +4699,8 @@
void f/*C*/(x) {}
}
''');
- var bReturnType = decoratedMethodType('f/*B*/').positionalParameters[0];
- var cReturnType = decoratedMethodType('f/*C*/').positionalParameters[0];
+ var bReturnType = decoratedMethodType('f/*B*/').positionalParameters![0]!;
+ var cReturnType = decoratedMethodType('f/*C*/').positionalParameters![0]!;
assertEdge(bReturnType.node, cReturnType.node, hard: true);
}
@@ -4696,8 +4713,8 @@
void f/*C*/({x = 0}) {}
}
''');
- var bReturnType = decoratedMethodType('f/*B*/').namedParameters['x'];
- var cReturnType = decoratedMethodType('f/*C*/').namedParameters['x'];
+ var bReturnType = decoratedMethodType('f/*B*/').namedParameters!['x']!;
+ var cReturnType = decoratedMethodType('f/*C*/').namedParameters!['x']!;
assertEdge(bReturnType.node, cReturnType.node, hard: true);
}
@@ -4710,8 +4727,8 @@
f/*C*/() => 1;
}
''');
- var bReturnType = decoratedMethodType('f/*B*/').returnType;
- var cReturnType = decoratedMethodType('f/*C*/').returnType;
+ var bReturnType = decoratedMethodType('f/*B*/').returnType!;
+ var cReturnType = decoratedMethodType('f/*C*/').returnType!;
assertEdge(cReturnType.node, bReturnType.node, hard: true);
}
@@ -5029,10 +5046,10 @@
await analyze('''
String f(void Function() g) => g.toString();
''');
- var toStringReturnType = variables
+ var toStringReturnType = variables!
.decoratedElementType(
- typeProvider.objectType.element.getMethod('toString'))
- .returnType;
+ typeProvider.objectType.element.getMethod('toString')!)
+ .returnType!;
assertEdge(
toStringReturnType.node, decoratedTypeAnnotation('String f').node,
hard: false);
@@ -5049,10 +5066,10 @@
''');
var nullable_i = decoratedTypeAnnotation('int i').node;
- var nullable_c_t = decoratedTypeAnnotation('C<int>').typeArguments[0].node;
+ var nullable_c_t = decoratedTypeAnnotation('C<int>').typeArguments[0]!.node;
var nullable_t = decoratedTypeAnnotation('T t').node;
- var check_i = checkExpression('i/*check*/');
- var nullable_c_t_or_nullable_t = check_i.checks.edges[FixReasonTarget.root]
+ var check_i = checkExpression('i/*check*/')!;
+ var nullable_c_t_or_nullable_t = check_i.checks.edges[FixReasonTarget.root]!
.destinationNode as NullabilityNodeForSubstitution;
expect(nullable_c_t_or_nullable_t.innerNode, same(nullable_c_t));
expect(nullable_c_t_or_nullable_t.outerNode, same(nullable_t));
@@ -5070,17 +5087,18 @@
var nullable_i = decoratedTypeAnnotation('int i').node;
var nullable_list_t =
- decoratedTypeAnnotation('List<int>').typeArguments[0].node;
- var addMethod = findNode.methodInvocation('x.add').methodName.staticElement;
- var nullable_t = variables
- .decoratedElementType(addMethod.declaration)
- .positionalParameters[0]
+ decoratedTypeAnnotation('List<int>').typeArguments[0]!.node;
+ var addMethod =
+ findNode.methodInvocation('x.add').methodName.staticElement!;
+ var nullable_t = variables!
+ .decoratedElementType(addMethod.declaration!)
+ .positionalParameters![0]!
.node;
assertEdge(nullable_t, never, hard: true, checkable: false);
- var check_i = checkExpression('i/*check*/');
+ var check_i = checkExpression('i/*check*/')!;
var nullable_list_t_or_nullable_t = check_i
.checks
- .edges[FixReasonTarget.root]
+ .edges[FixReasonTarget.root]!
.destinationNode as NullabilityNodeForSubstitution;
expect(nullable_list_t_or_nullable_t.innerNode, same(nullable_list_t));
expect(nullable_list_t_or_nullable_t.outerNode, same(nullable_t));
@@ -5098,8 +5116,8 @@
var nullable_i = decoratedTypeAnnotation('int i').node;
var nullable_f_t = decoratedTypeAnnotation('int>').node;
var nullable_t = decoratedTypeAnnotation('T t').node;
- var check_i = checkExpression('i/*check*/');
- var nullable_f_t_or_nullable_t = check_i.checks.edges[FixReasonTarget.root]
+ var check_i = checkExpression('i/*check*/')!;
+ var nullable_f_t_or_nullable_t = check_i.checks.edges[FixReasonTarget.root]!
.destinationNode as NullabilityNodeForSubstitution;
expect(nullable_f_t_or_nullable_t.innerNode, same(nullable_f_t));
expect(nullable_f_t_or_nullable_t.outerNode, same(nullable_t));
@@ -5120,7 +5138,7 @@
decoratedTypeAnnotation('int/*1*/').node,
hard: true, checkable: false);
assertNullCheck(
- checkExpression('c/*check*/'),
+ checkExpression('c/*check*/')!,
assertEdge(decoratedTypeAnnotation('C<int/*3*/>/*4*/').node,
decoratedTypeAnnotation('C<int/*1*/>/*2*/').node,
hard: true));
@@ -5137,7 +5155,7 @@
''');
var nullable_i = decoratedTypeAnnotation('int i').node;
var nullable_j = decoratedTypeAnnotation('int j').node;
- assertNullCheck(checkExpression('j/*check*/'),
+ assertNullCheck(checkExpression('j/*check*/')!,
assertEdge(nullable_j, nullable_i, hard: true));
}
@@ -5154,7 +5172,7 @@
}
''');
var nullable_j = decoratedTypeAnnotation('int j');
- assertNullCheck(checkExpression('j/*check*/'),
+ assertNullCheck(checkExpression('j/*check*/')!,
assertEdge(nullable_j.node, inSet(pointsToNever), hard: true));
}
@@ -5211,10 +5229,10 @@
T f<T extends Object>(T t) => t;
int g() => (f<int>(1));
''');
- var check_i = checkExpression('(f<int>(1))');
+ var check_i = checkExpression('(f<int>(1))')!;
var t_bound = decoratedTypeAnnotation('Object').node;
var nullable_f_t = decoratedTypeAnnotation('int>').node;
- var nullable_f_t_or_nullable_t = check_i.checks.edges[FixReasonTarget.root]
+ var nullable_f_t_or_nullable_t = check_i.checks.edges[FixReasonTarget.root]!
.sourceNode as NullabilityNodeForSubstitution;
var nullable_t = decoratedTypeAnnotation('T f').node;
expect(nullable_f_t_or_nullable_t.innerNode, same(nullable_f_t));
@@ -5233,7 +5251,7 @@
bool f(C c) => (c?.m());
''');
var lubNode =
- decoratedExpressionType('(c?.m())').node as NullabilityNodeForLUB;
+ decoratedExpressionType('(c?.m())')!.node as NullabilityNodeForLUB;
expect(lubNode.left, same(decoratedTypeAnnotation('C c').node));
expect(lubNode.right, same(decoratedTypeAnnotation('bool m').node));
assertEdge(lubNode, decoratedTypeAnnotation('bool f').node, hard: false);
@@ -5264,7 +5282,7 @@
}
''');
- assertNullCheck(checkExpression('c.m'),
+ assertNullCheck(checkExpression('c.m')!,
assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true));
}
@@ -5278,7 +5296,7 @@
}
''');
- assertNullCheck(checkExpression('c..m'),
+ assertNullCheck(checkExpression('c..m')!,
assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true));
}
@@ -5441,12 +5459,12 @@
void f(void g(int i)/*2*/) {}
}
''');
- var p1 = variables.decoratedElementType(findNode
+ var p1 = variables!.decoratedElementType(findNode
.functionTypedFormalParameter('void g(int i)/*1*/')
- .declaredElement);
- var p2 = variables.decoratedElementType(findNode
+ .declaredElement!);
+ var p2 = variables!.decoratedElementType(findNode
.functionTypedFormalParameter('void g(int i)/*2*/')
- .declaredElement);
+ .declaredElement!);
assertEdge(p1.node, p2.node, hard: false, checkable: false);
}
@@ -5690,7 +5708,7 @@
''');
assertNullCheck(
- checkExpression('(null)'),
+ checkExpression('(null)')!,
assertEdge(inSet(alwaysPlus), decoratedTypeAnnotation('int').node,
hard: false));
}
@@ -5810,11 +5828,11 @@
}
''');
- assertNullCheck(checkExpression('b1/*check*/'),
+ assertNullCheck(checkExpression('b1/*check*/')!,
assertEdge(decoratedTypeAnnotation('bool b1').node, never, hard: true));
- assertNullCheck(checkExpression('b2/*check*/'),
+ assertNullCheck(checkExpression('b2/*check*/')!,
assertEdge(decoratedTypeAnnotation('bool b2').node, never, hard: true));
- assertNullCheck(checkExpression('c.m'),
+ assertNullCheck(checkExpression('c.m')!,
assertEdge(decoratedTypeAnnotation('C c').node, never, hard: false));
}
@@ -5835,11 +5853,11 @@
}
''');
- assertNullCheck(checkExpression('b1/*check*/'),
+ assertNullCheck(checkExpression('b1/*check*/')!,
assertEdge(decoratedTypeAnnotation('bool b1').node, never, hard: true));
- assertNullCheck(checkExpression('b2/*check*/'),
+ assertNullCheck(checkExpression('b2/*check*/')!,
assertEdge(decoratedTypeAnnotation('bool b2').node, never, hard: true));
- assertNullCheck(checkExpression('c.m'),
+ assertNullCheck(checkExpression('c.m')!,
assertEdge(decoratedTypeAnnotation('C c').node, never, hard: false));
}
@@ -5857,9 +5875,9 @@
}
''');
- assertNullCheck(checkExpression('b/*check*/'),
+ assertNullCheck(checkExpression('b/*check*/')!,
assertEdge(decoratedTypeAnnotation('bool b').node, never, hard: false));
- assertNullCheck(checkExpression('c.m'),
+ assertNullCheck(checkExpression('c.m')!,
assertEdge(decoratedTypeAnnotation('C c').node, never, hard: false));
}
@@ -5879,13 +5897,13 @@
}
''');
- assertNullCheck(checkExpression('b/*check*/'),
+ assertNullCheck(checkExpression('b/*check*/')!,
assertEdge(decoratedTypeAnnotation('bool b').node, never, hard: true));
- assertNullCheck(checkExpression('c1.m'),
+ assertNullCheck(checkExpression('c1.m')!,
assertEdge(decoratedTypeAnnotation('C c1').node, never, hard: true));
- assertNullCheck(checkExpression('c2.m'),
+ assertNullCheck(checkExpression('c2.m')!,
assertEdge(decoratedTypeAnnotation('C c2').node, never, hard: true));
- assertNullCheck(checkExpression('c3.m'),
+ assertNullCheck(checkExpression('c3.m')!,
assertEdge(decoratedTypeAnnotation('C c3').node, never, hard: true));
}
@@ -5900,11 +5918,11 @@
}
''');
- assertNullCheck(checkExpression('b1/*check*/'),
+ assertNullCheck(checkExpression('b1/*check*/')!,
assertEdge(decoratedTypeAnnotation('bool b1').node, never, hard: true));
- assertNullCheck(checkExpression('c1.m'),
+ assertNullCheck(checkExpression('c1.m')!,
assertEdge(decoratedTypeAnnotation('C c1').node, never, hard: false));
- assertNullCheck(checkExpression('c2.m'),
+ assertNullCheck(checkExpression('c2.m')!,
assertEdge(decoratedTypeAnnotation('C c2').node, never, hard: false));
}
@@ -5920,12 +5938,12 @@
''');
assertNullCheck(
- checkExpression('l/*check*/'),
+ checkExpression('l/*check*/')!,
assertEdge(decoratedTypeAnnotation('List<C> l').node, never,
hard: true));
- assertNullCheck(checkExpression('c1.m'),
+ assertNullCheck(checkExpression('c1.m')!,
assertEdge(decoratedTypeAnnotation('C c1').node, never, hard: false));
- assertNullCheck(checkExpression('c2.m'),
+ assertNullCheck(checkExpression('c2.m')!,
assertEdge(decoratedTypeAnnotation('C c2').node, never, hard: false));
}
@@ -5945,14 +5963,14 @@
''');
assertNullCheck(
- checkExpression('l/*check*/'),
+ checkExpression('l/*check*/')!,
assertEdge(decoratedTypeAnnotation('List<C> l').node, never,
hard: true));
- assertNullCheck(checkExpression('c1.m'),
+ assertNullCheck(checkExpression('c1.m')!,
assertEdge(decoratedTypeAnnotation('C c1').node, never, hard: false));
- assertNullCheck(checkExpression('c2.m'),
+ assertNullCheck(checkExpression('c2.m')!,
assertEdge(decoratedTypeAnnotation('C c2').node, never, hard: true));
- assertNullCheck(checkExpression('c3.m'),
+ assertNullCheck(checkExpression('c3.m')!,
assertEdge(decoratedTypeAnnotation('C c3').node, never, hard: false));
}
@@ -5973,13 +5991,13 @@
}
''');
- assertNullCheck(checkExpression('b1/*check*/'),
+ assertNullCheck(checkExpression('b1/*check*/')!,
assertEdge(decoratedTypeAnnotation('bool b1').node, never, hard: true));
- assertNullCheck(checkExpression('c4.m'),
+ assertNullCheck(checkExpression('c4.m')!,
assertEdge(decoratedTypeAnnotation('C c4').node, never, hard: true));
- assertNullCheck(checkExpression('c2.m'),
+ assertNullCheck(checkExpression('c2.m')!,
assertEdge(decoratedTypeAnnotation('C c2').node, never, hard: false));
- assertNullCheck(checkExpression('c3.m'),
+ assertNullCheck(checkExpression('c3.m')!,
assertEdge(decoratedTypeAnnotation('C c3').node, never, hard: false));
}
@@ -5999,17 +6017,17 @@
}
''');
- assertNullCheck(checkExpression('b1/*check*/'),
+ assertNullCheck(checkExpression('b1/*check*/')!,
assertEdge(decoratedTypeAnnotation('bool b1').node, never, hard: true));
//TODO(mfairhurst): enable this check
//assertNullCheck(checkExpression('b2/*check*/'),
// assertEdge(decoratedTypeAnnotation('bool b2').node, never, hard: true));
//assertEdge(decoratedTypeAnnotation('b3 =').node, never, hard: false);
- assertNullCheck(checkExpression('c1.m'),
+ assertNullCheck(checkExpression('c1.m')!,
assertEdge(decoratedTypeAnnotation('C c1').node, never, hard: false));
- assertNullCheck(checkExpression('c2.m'),
+ assertNullCheck(checkExpression('c2.m')!,
assertEdge(decoratedTypeAnnotation('C c2').node, never, hard: true));
- assertNullCheck(checkExpression('c3.m'),
+ assertNullCheck(checkExpression('c3.m')!,
assertEdge(decoratedTypeAnnotation('C c3').node, never, hard: false));
}
@@ -6024,13 +6042,13 @@
}
''');
- assertNullCheck(checkExpression('b)'),
+ assertNullCheck(checkExpression('b)')!,
assertEdge(decoratedTypeAnnotation('bool b').node, never, hard: true));
- assertNullCheck(checkExpression('c1.m'),
+ assertNullCheck(checkExpression('c1.m')!,
assertEdge(decoratedTypeAnnotation('C c1').node, never, hard: false));
- assertNullCheck(checkExpression('c2.m'),
+ assertNullCheck(checkExpression('c2.m')!,
assertEdge(decoratedTypeAnnotation('C c2').node, never, hard: false));
- assertNullCheck(checkExpression('c3.m'),
+ assertNullCheck(checkExpression('c3.m')!,
assertEdge(decoratedTypeAnnotation('C c3').node, never, hard: true));
}
@@ -6055,15 +6073,15 @@
}
''');
- assertNullCheck(checkExpression('b/*check*/'),
+ assertNullCheck(checkExpression('b/*check*/')!,
assertEdge(decoratedTypeAnnotation('bool b').node, never, hard: true));
- assertNullCheck(checkExpression('c1.m'),
+ assertNullCheck(checkExpression('c1.m')!,
assertEdge(decoratedTypeAnnotation('C c1').node, never, hard: false));
- assertNullCheck(checkExpression('c2.m'),
+ assertNullCheck(checkExpression('c2.m')!,
assertEdge(decoratedTypeAnnotation('C c2').node, never, hard: false));
- assertNullCheck(checkExpression('c3.m'),
+ assertNullCheck(checkExpression('c3.m')!,
assertEdge(decoratedTypeAnnotation('C c3').node, never, hard: true));
- assertNullCheck(checkExpression('c4.m'),
+ assertNullCheck(checkExpression('c4.m')!,
assertEdge(decoratedTypeAnnotation('C c4').node, never, hard: false));
}
@@ -6088,15 +6106,15 @@
}
''');
- assertNullCheck(checkExpression('b/*check*/'),
+ assertNullCheck(checkExpression('b/*check*/')!,
assertEdge(decoratedTypeAnnotation('bool b').node, never, hard: true));
- assertNullCheck(checkExpression('c1.m'),
+ assertNullCheck(checkExpression('c1.m')!,
assertEdge(decoratedTypeAnnotation('C c1').node, never, hard: false));
- assertNullCheck(checkExpression('c2.m'),
+ assertNullCheck(checkExpression('c2.m')!,
assertEdge(decoratedTypeAnnotation('C c2').node, never, hard: true));
- assertNullCheck(checkExpression('c3.m'),
+ assertNullCheck(checkExpression('c3.m')!,
assertEdge(decoratedTypeAnnotation('C c3').node, never, hard: true));
- assertNullCheck(checkExpression('c4.m'),
+ assertNullCheck(checkExpression('c4.m')!,
assertEdge(decoratedTypeAnnotation('C c4').node, never, hard: true));
}
@@ -6110,7 +6128,7 @@
}
''');
- assertNullCheck(checkExpression('c.m'),
+ assertNullCheck(checkExpression('c.m')!,
assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true));
}
@@ -6133,13 +6151,13 @@
}
''');
- assertNullCheck(checkExpression('b1/*check*/'),
+ assertNullCheck(checkExpression('b1/*check*/')!,
assertEdge(decoratedTypeAnnotation('bool b1').node, never, hard: true));
- assertNullCheck(checkExpression('b2/*check*/'),
+ assertNullCheck(checkExpression('b2/*check*/')!,
assertEdge(decoratedTypeAnnotation('bool b2').node, never, hard: true));
- assertNullCheck(checkExpression('c1.m'),
+ assertNullCheck(checkExpression('c1.m')!,
assertEdge(decoratedTypeAnnotation('C c1').node, never, hard: false));
- assertNullCheck(checkExpression('c2.m'),
+ assertNullCheck(checkExpression('c2.m')!,
assertEdge(decoratedTypeAnnotation('C c2').node, never, hard: false));
}
@@ -6187,10 +6205,10 @@
}
''');
- assertNullCheck(checkExpression('i1.toDouble'),
+ assertNullCheck(checkExpression('i1.toDouble')!,
assertEdge(decoratedTypeAnnotation('int i1').node, never, hard: false));
- assertNullCheck(checkExpression('i2.toDouble'),
+ assertNullCheck(checkExpression('i2.toDouble')!,
assertEdge(decoratedTypeAnnotation('int i2').node, never, hard: false));
}
@@ -6205,16 +6223,16 @@
}
''');
- assertNullCheck(checkExpression('c1.m'),
+ assertNullCheck(checkExpression('c1.m')!,
assertEdge(decoratedTypeAnnotation('C c1').node, never, hard: true));
- assertNullCheck(checkExpression('c3.m'),
+ assertNullCheck(checkExpression('c3.m')!,
assertEdge(decoratedTypeAnnotation('C c3').node, never, hard: true));
- assertNullCheck(checkExpression('c2.m'),
+ assertNullCheck(checkExpression('c2.m')!,
assertEdge(decoratedTypeAnnotation('C c2').node, never, hard: false));
- assertNullCheck(checkExpression('c4.m'),
+ assertNullCheck(checkExpression('c4.m')!,
assertEdge(decoratedTypeAnnotation('C c4').node, never, hard: false));
}
@@ -6230,7 +6248,7 @@
}
''');
- assertNullCheck(checkExpression('c.m'),
+ assertNullCheck(checkExpression('c.m')!,
assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true));
}
@@ -6251,9 +6269,9 @@
}
''');
- assertNullCheck(checkExpression('b/*check*/'),
+ assertNullCheck(checkExpression('b/*check*/')!,
assertEdge(decoratedTypeAnnotation('bool b').node, never, hard: false));
- assertNullCheck(checkExpression('c.m'),
+ assertNullCheck(checkExpression('c.m')!,
assertEdge(decoratedTypeAnnotation('C c').node, never, hard: false));
}
@@ -6272,9 +6290,9 @@
}
''');
- assertNullCheck(checkExpression('b/*check*/'),
+ assertNullCheck(checkExpression('b/*check*/')!,
assertEdge(decoratedTypeAnnotation('bool b').node, never, hard: true));
- assertNullCheck(checkExpression('c.m'),
+ assertNullCheck(checkExpression('c.m')!,
assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true));
}
@@ -6342,16 +6360,16 @@
}
''');
- assertNullCheck(checkExpression('c1.m'),
+ assertNullCheck(checkExpression('c1.m')!,
assertEdge(decoratedTypeAnnotation('C c1').node, never, hard: true));
- assertNullCheck(checkExpression('c4.m'),
+ assertNullCheck(checkExpression('c4.m')!,
assertEdge(decoratedTypeAnnotation('C c4').node, never, hard: true));
- assertNullCheck(checkExpression('c2.m'),
+ assertNullCheck(checkExpression('c2.m')!,
assertEdge(decoratedTypeAnnotation('C c2').node, never, hard: false));
- assertNullCheck(checkExpression('c3.m'),
+ assertNullCheck(checkExpression('c3.m')!,
assertEdge(decoratedTypeAnnotation('C c3').node, never, hard: false));
}
@@ -6384,13 +6402,13 @@
}
''');
- assertNullCheck(checkExpression('b/*check*/'),
+ assertNullCheck(checkExpression('b/*check*/')!,
assertEdge(decoratedTypeAnnotation('bool b').node, never, hard: true));
- assertNullCheck(checkExpression('c1.m'),
+ assertNullCheck(checkExpression('c1.m')!,
assertEdge(decoratedTypeAnnotation('C c1').node, never, hard: false));
- assertNullCheck(checkExpression('c2.m'),
+ assertNullCheck(checkExpression('c2.m')!,
assertEdge(decoratedTypeAnnotation('C c2').node, never, hard: true));
- assertNullCheck(checkExpression('c3.m'),
+ assertNullCheck(checkExpression('c3.m')!,
assertEdge(decoratedTypeAnnotation('C c3').node, never, hard: true));
}
@@ -6402,7 +6420,7 @@
''');
var declaration = decoratedTypeAnnotation('int i').node;
- var use = checkExpression('i--');
+ var use = checkExpression('i--')!;
assertNullCheck(use, assertEdge(declaration, never, hard: true));
var returnType = decoratedTypeAnnotation('int f').node;
@@ -6417,7 +6435,7 @@
''');
var declaration = decoratedTypeAnnotation('int i').node;
- var use = checkExpression('i++');
+ var use = checkExpression('i++')!;
assertNullCheck(use, assertEdge(declaration, never, hard: true));
var returnType = decoratedTypeAnnotation('int f').node;
@@ -6448,9 +6466,9 @@
var cType = decoratedTypeAnnotation('C<int> c');
var returnType = decoratedTypeAnnotation('C<int> f');
assertNullCheck(
- checkExpression('c++'), assertEdge(cType.node, never, hard: true));
+ checkExpression('c++')!, assertEdge(cType.node, never, hard: true));
assertEdge(cType.node, returnType.node, hard: false);
- assertEdge(cType.typeArguments[0].node, returnType.typeArguments[0].node,
+ assertEdge(cType.typeArguments[0]!.node, returnType.typeArguments[0]!.node,
hard: false, checkable: false);
}
@@ -6558,7 +6576,7 @@
}
''');
- assertNullCheck(checkExpression('c.x'),
+ assertNullCheck(checkExpression('c.x')!,
assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true));
}
@@ -6583,13 +6601,13 @@
}
int Function(int) g(C c) => c.f;
''');
- var fType = variables.decoratedElementType(findElement.method('f'));
+ var fType = variables!.decoratedElementType(findElement.method('f'));
var gReturnType =
- variables.decoratedElementType(findElement.function('g')).returnType;
- assertEdge(fType.returnType.node, gReturnType.returnType.node,
+ variables!.decoratedElementType(findElement.function('g')).returnType!;
+ assertEdge(fType.returnType!.node, gReturnType.returnType!.node,
hard: false, checkable: false);
- assertEdge(gReturnType.positionalParameters[0].node,
- fType.positionalParameters[0].node,
+ assertEdge(gReturnType.positionalParameters![0]!.node,
+ fType.positionalParameters![0]!.node,
hard: false, checkable: false);
}
@@ -6601,7 +6619,7 @@
''');
var nullable_b = decoratedTypeAnnotation('bool b').node;
- var check_b = checkExpression('b;');
+ var check_b = checkExpression('b;')!;
assertNullCheck(check_b, assertEdge(nullable_b, never, hard: true));
var return_f = decoratedTypeAnnotation('bool f').node;
@@ -6628,7 +6646,7 @@
assertEdge(decoratedTypeAnnotation('C operator').node,
decoratedTypeAnnotation('C test').node,
hard: false);
- assertNullCheck(checkExpression('c/*check*/'),
+ assertNullCheck(checkExpression('c/*check*/')!,
assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true));
}
@@ -6652,12 +6670,12 @@
var cType = decoratedTypeAnnotation('C<int> c');
var testReturnType = decoratedTypeAnnotation('List<int> test');
assertEdge(operatorReturnType.node, testReturnType.node, hard: false);
- assertNullCheck(checkExpression('c/*check*/'),
+ assertNullCheck(checkExpression('c/*check*/')!,
assertEdge(cType.node, never, hard: true));
assertEdge(
- substitutionNode(cType.typeArguments[0].node,
- operatorReturnType.typeArguments[0].node),
- testReturnType.typeArguments[0].node,
+ substitutionNode(cType.typeArguments[0]!.node,
+ operatorReturnType.typeArguments[0]!.node),
+ testReturnType.typeArguments[0]!.node,
hard: false,
checkable: false);
}
@@ -6670,7 +6688,7 @@
''');
var declaration = decoratedTypeAnnotation('int i').node;
- var use = checkExpression('i;');
+ var use = checkExpression('i;')!;
assertNullCheck(use, assertEdge(declaration, never, hard: true));
var returnType = decoratedTypeAnnotation('int f').node;
@@ -6685,7 +6703,7 @@
''');
var declaration = decoratedTypeAnnotation('int i').node;
- var use = checkExpression('i;');
+ var use = checkExpression('i;')!;
assertNullCheck(use, assertEdge(declaration, never, hard: true));
var returnType = decoratedTypeAnnotation('int f').node;
@@ -6715,9 +6733,9 @@
assertEdge(xType.node, never, hard: true);
assertEdge(plusReturnType.node, fReturnType.node, hard: false);
assertEdge(
- substitutionNode(
- xType.typeArguments[0].node, plusReturnType.typeArguments[0].node),
- fReturnType.typeArguments[0].node,
+ substitutionNode(xType.typeArguments[0]!.node,
+ plusReturnType.typeArguments[0]!.node),
+ fReturnType.typeArguments[0]!.node,
hard: false,
checkable: false);
}
@@ -6909,10 +6927,10 @@
Future<void> test_propertyAccess_object_property_on_function_type() async {
await analyze('int f(void Function() g) => g.hashCode;');
- var hashCodeReturnType = variables
+ var hashCodeReturnType = variables!
.decoratedElementType(
- typeProvider.objectType.element.getGetter('hashCode'))
- .returnType;
+ typeProvider.objectType.element.getGetter('hashCode')!)
+ .returnType!;
assertEdge(hashCodeReturnType.node, decoratedTypeAnnotation('int f').node,
hard: false);
}
@@ -6937,7 +6955,7 @@
bool f(C c) => (c?.b);
''');
var lubNode =
- decoratedExpressionType('(c?.b)').node as NullabilityNodeForLUB;
+ decoratedExpressionType('(c?.b)')!.node as NullabilityNodeForLUB;
expect(lubNode.left, same(decoratedTypeAnnotation('C c').node));
expect(lubNode.right, same(decoratedTypeAnnotation('bool get b').node));
assertEdge(lubNode, decoratedTypeAnnotation('bool f').node, hard: false);
@@ -6965,7 +6983,7 @@
}
''');
- assertNullCheck(checkExpression('c).x'),
+ assertNullCheck(checkExpression('c).x')!,
assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true));
}
@@ -7235,7 +7253,8 @@
var edge = assertEdge(
inSet(alwaysPlus), decoratedTypeAnnotation('int').node,
hard: false);
- expect(edge.sourceNode.displayName, 'implicit null return (test.dart:2:3)');
+ expect(
+ edge.sourceNode!.displayName, 'implicit null return (test.dart:2:3)');
}
Future<void> test_return_in_asyncStar() async {
@@ -7268,8 +7287,8 @@
var edge = assertEdge(
inSet(alwaysPlus), decoratedTypeAnnotation('int').node,
hard: false);
- assertNullCheck(checkExpression('null'), edge);
- expect(edge.sourceNode.displayName, 'null literal (test.dart:2:10)');
+ assertNullCheck(checkExpression('null')!, edge);
+ expect(edge.sourceNode!.displayName, 'null literal (test.dart:2:10)');
}
Future<void> test_return_null_generic() async {
@@ -7282,7 +7301,7 @@
''');
var tNode = decoratedTypeAnnotation('T f').node;
assertEdge(inSet(alwaysPlus), tNode, hard: false);
- assertNullCheck(checkExpression('null'),
+ assertNullCheck(checkExpression('null')!,
assertEdge(inSet(alwaysPlus), tNode, hard: false));
}
@@ -7300,11 +7319,12 @@
assertNoUpstreamNullability(mapNode);
var keyEdge = assertEdge(anyNode, keyNode, hard: true, checkable: false);
assertNoUpstreamNullability(keyEdge.sourceNode);
- expect(keyEdge.sourceNode.displayName, 'map key type (test.dart:2:10)');
+ expect(keyEdge.sourceNode!.displayName, 'map key type (test.dart:2:10)');
var valueEdge =
assertEdge(anyNode, valueNode, hard: true, checkable: false);
assertNoUpstreamNullability(valueEdge.sourceNode);
- expect(valueEdge.sourceNode.displayName, 'map value type (test.dart:2:10)');
+ expect(
+ valueEdge.sourceNode!.displayName, 'map value type (test.dart:2:10)');
}
Future<void> test_setOrMapLiteral_map_noTypeArgument_nullableKey() async {
@@ -7436,7 +7456,7 @@
assertNoUpstreamNullability(setNode);
var edge = assertEdge(anyNode, valueNode, hard: true, checkable: false);
assertNoUpstreamNullability(edge.sourceNode);
- expect(edge.sourceNode.displayName, 'set element type (test.dart:2:10)');
+ expect(edge.sourceNode!.displayName, 'set element type (test.dart:2:10)');
}
Future<void> test_setOrMapLiteral_set_noTypeArgument_nullableElement() async {
@@ -7530,7 +7550,7 @@
''');
assertNullCheck(
- checkExpression('j;'),
+ checkExpression('j;')!,
assertEdge(decoratedTypeAnnotation('int j').node,
decoratedTypeAnnotation('int i').node,
hard: false, isSetupAssignment: true));
@@ -7557,7 +7577,7 @@
assertNoEdge(graph.never, decoratedTypeAnnotation('int/*1*/').node);
assertNullCheck(
- checkExpression('j;'),
+ checkExpression('j;')!,
assertEdge(decoratedTypeAnnotation('int j').node,
decoratedTypeAnnotation('int/*2*/').node,
hard: false, isSetupAssignment: true));
@@ -7625,7 +7645,7 @@
var iNullable = decoratedTypeAnnotation('int i').node;
assertNullCheck(
- checkExpression('j;'),
+ checkExpression('j;')!,
assertEdge(decoratedTypeAnnotation('int j').node, iNullable,
hard: false, guards: [iNullable], isSetupAssignment: true));
}
@@ -7674,13 +7694,13 @@
int f(int i) => 0;
int Function(int) g() => f;
''');
- var fType = variables.decoratedElementType(findElement.function('f'));
+ var fType = variables!.decoratedElementType(findElement.function('f'));
var gReturnType =
- variables.decoratedElementType(findElement.function('g')).returnType;
- assertEdge(fType.returnType.node, gReturnType.returnType.node,
+ variables!.decoratedElementType(findElement.function('g')).returnType!;
+ assertEdge(fType.returnType!.node, gReturnType.returnType!.node,
hard: false, checkable: false);
- assertEdge(gReturnType.positionalParameters[0].node,
- fType.positionalParameters[0].node,
+ assertEdge(gReturnType.positionalParameters![0]!.node,
+ fType.positionalParameters![0]!.node,
hard: false, checkable: false);
}
@@ -7691,13 +7711,13 @@
int Function(int) g() => f;
}
''');
- var fType = variables.decoratedElementType(findElement.method('f'));
+ var fType = variables!.decoratedElementType(findElement.method('f'));
var gReturnType =
- variables.decoratedElementType(findElement.method('g')).returnType;
- assertEdge(fType.returnType.node, gReturnType.returnType.node,
+ variables!.decoratedElementType(findElement.method('g')).returnType!;
+ assertEdge(fType.returnType!.node, gReturnType.returnType!.node,
hard: false, checkable: false);
- assertEdge(gReturnType.positionalParameters[0].node,
- fType.positionalParameters[0].node,
+ assertEdge(gReturnType.positionalParameters![0]!.node,
+ fType.positionalParameters![0]!.node,
hard: false, checkable: false);
}
@@ -7921,7 +7941,7 @@
var intNode = decoratedTypeAnnotation('int').node;
assertNoUpstreamNullability(intNode);
var edge = assertEdge(anyNode, intNode, hard: false);
- expect(edge.sourceNode.displayName, 'throw expression (test.dart:2:10)');
+ expect(edge.sourceNode!.displayName, 'throw expression (test.dart:2:10)');
}
Future<void> test_top_level_annotation_begins_flow_analysis() async {
@@ -7997,7 +8017,7 @@
var x = f();
''');
var xType =
- variables.decoratedElementType(findNode.simple('x').staticElement);
+ variables!.decoratedElementType(findNode.simple('x').staticElement!);
assertEdge(decoratedTypeAnnotation('int').node, xType.node, hard: false);
}
@@ -8090,8 +8110,8 @@
var pointClass =
findNode.typeName('Point').name.staticElement as ClassElement;
var pointBound =
- variables.decoratedTypeParameterBound(pointClass.typeParameters[0]);
- _assertType(pointBound.type, 'num');
+ variables!.decoratedTypeParameterBound(pointClass.typeParameters[0])!;
+ _assertType(pointBound.type!, 'num');
assertEdge(decoratedTypeAnnotation('int>').node, pointBound.node,
hard: true);
}
@@ -8102,8 +8122,8 @@
''');
var listClass = typeProvider.listElement;
var listBound =
- variables.decoratedTypeParameterBound(listClass.typeParameters[0]);
- _assertType(listBound.type, 'dynamic');
+ variables!.decoratedTypeParameterBound(listClass.typeParameters[0])!;
+ _assertType(listBound.type!, 'dynamic');
assertEdge(decoratedTypeAnnotation('int>').node, listBound.node,
hard: true);
}
@@ -8175,7 +8195,7 @@
''');
var cType = decoratedTypeAnnotation('C c');
var cBound = decoratedTypeAnnotation('Object');
- assertEdge(cType.typeArguments[0].node, cBound.node, hard: true);
+ assertEdge(cType.typeArguments[0]!.node, cBound.node, hard: true);
}
Future<void> test_typeName_with_bound_function_type() async {
@@ -8185,8 +8205,9 @@
''');
var cType = decoratedTypeAnnotation('C c');
var cBound = decoratedGenericFunctionTypeAnnotation('int Function()');
- assertEdge(cType.typeArguments[0].node, cBound.node, hard: true);
- assertEdge(cType.typeArguments[0].returnType.node, cBound.returnType.node,
+ assertEdge(cType.typeArguments[0]!.node, cBound.node, hard: true);
+ assertEdge(
+ cType.typeArguments[0]!.returnType!.node, cBound.returnType!.node,
hard: true);
}
@@ -8198,8 +8219,8 @@
var cType = decoratedTypeAnnotation('C c');
var tBound = decoratedTypeAnnotation('Object,');
var uBound = decoratedTypeAnnotation('Object>');
- assertEdge(cType.typeArguments[0].node, tBound.node, hard: true);
- assertEdge(cType.typeArguments[1].node, uBound.node, hard: true);
+ assertEdge(cType.typeArguments[0]!.node, tBound.node, hard: true);
+ assertEdge(cType.typeArguments[1]!.node, uBound.node, hard: true);
}
Future<void> test_variableDeclaration() async {
@@ -8220,13 +8241,13 @@
}
class _DecoratedClassHierarchyForTesting implements DecoratedClassHierarchy {
- AssignmentCheckerTest assignmentCheckerTest;
+ late AssignmentCheckerTest assignmentCheckerTest;
@override
- DecoratedType asInstanceOf(DecoratedType type, ClassElement superclass) {
+ DecoratedType asInstanceOf(DecoratedType type, ClassElement? superclass) {
var class_ = (type.type as InterfaceType).element;
if (class_ == superclass) return type;
- if (superclass.name == 'Object') {
+ if (superclass!.name == 'Object') {
return DecoratedType(
superclass.instantiate(
typeArguments: const [],
@@ -8242,7 +8263,7 @@
if (class_.name == 'List' && superclass.name == 'Iterable') {
return DecoratedType(
superclass.instantiate(
- typeArguments: [type.typeArguments[0].type],
+ typeArguments: [type.typeArguments[0]!.type!],
nullabilitySuffix: NullabilitySuffix.star,
),
type.node,
@@ -8252,7 +8273,7 @@
if (class_.name == 'Future' && superclass.name == 'FutureOr') {
return DecoratedType(
superclass.instantiate(
- typeArguments: [type.typeArguments[0].type],
+ typeArguments: [type.typeArguments[0]!.type!],
nullabilitySuffix: NullabilitySuffix.star,
),
type.node,
@@ -8284,13 +8305,13 @@
const _TestEdgeOrigin();
@override
- CodeReference get codeReference => null;
+ CodeReference? get codeReference => null;
@override
String get description => 'Test edge';
@override
- EdgeOriginKind get kind => null;
+ EdgeOriginKind? get kind => null;
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
diff --git a/pkg/nnbd_migration/test/edit_plan_test.dart b/pkg/nnbd_migration/test/edit_plan_test.dart
index d7881a2..3cc00e7 100644
--- a/pkg/nnbd_migration/test/edit_plan_test.dart
+++ b/pkg/nnbd_migration/test/edit_plan_test.dart
@@ -24,14 +24,14 @@
@reflectiveTest
class EditPlanTest extends AbstractSingleUnitTest {
- String code;
+ String? code;
- EditPlanner _planner;
+ EditPlanner? _planner;
@override
bool get analyzeWithNnbd => true;
- EditPlanner get planner {
+ EditPlanner? get planner {
if (_planner == null) createPlanner();
return _planner;
}
@@ -41,54 +41,54 @@
await resolveTestUnit(code);
}
- Map<int, List<AtomicEdit>> checkPlan(EditPlan plan, String expected,
- {String expectedIncludingInformative}) {
+ Map<int?, List<AtomicEdit>> checkPlan(EditPlan plan, String expected,
+ {String? expectedIncludingInformative}) {
expectedIncludingInformative ??= expected;
- var changes = planner.finalize(plan);
- expect(changes.applyTo(code), expected);
- expect(changes.applyTo(code, includeInformative: true),
+ var changes = planner!.finalize(plan)!;
+ expect(changes.applyTo(code!), expected);
+ expect(changes.applyTo(code!, includeInformative: true),
expectedIncludingInformative);
return changes;
}
void createPlanner({bool removeViaComments = false}) {
- _planner = EditPlanner(testUnit.lineInfo, code,
+ _planner = EditPlanner(testUnit!.lineInfo, code,
removeViaComments: removeViaComments);
}
- NodeProducingEditPlan extract(AstNode inner, AstNode outer) =>
- planner.extract(outer, planner.passThrough(inner));
+ NodeProducingEditPlan extract(AstNode inner, AstNode? outer) =>
+ planner!.extract(outer, planner!.passThrough(inner));
Future<void> test_acceptLateHint() async {
var code = '/* late */ int x = 0;';
await analyze(code);
- var hint = getPrefixHint(findNode.simple('int').token);
+ var hint = getPrefixHint(findNode.simple('int').token)!;
var changes = checkPlan(
- planner.acceptPrefixHint(
- planner.passThrough(findNode.simple('int')), hint),
+ planner!.acceptPrefixHint(
+ planner!.passThrough(findNode.simple('int')), hint),
'late int x = 0;');
expect(changes.keys, unorderedEquals([0, 7]));
expect(changes[7], hasLength(1));
- expect(changes[7][0].length, 3);
+ expect(changes[7]![0].length, 3);
}
Future<void> test_acceptLateHint_space_needed_after() async {
var code = '/* late */int x = 0;';
await analyze(code);
- var hint = getPrefixHint(findNode.simple('int').token);
+ var hint = getPrefixHint(findNode.simple('int').token)!;
checkPlan(
- planner.acceptPrefixHint(
- planner.passThrough(findNode.simple('int')), hint),
+ planner!.acceptPrefixHint(
+ planner!.passThrough(findNode.simple('int')), hint),
'late int x = 0;');
}
Future<void> test_acceptLateHint_space_needed_before() async {
var code = '@deprecated/* late */ int x = 0;';
await analyze(code);
- var hint = getPrefixHint(findNode.simple('int').token);
+ var hint = getPrefixHint(findNode.simple('int').token)!;
checkPlan(
- planner.acceptPrefixHint(
- planner.passThrough(findNode.simple('int')), hint),
+ planner!.acceptPrefixHint(
+ planner!.passThrough(findNode.simple('int')), hint),
'@deprecated late int x = 0;');
}
@@ -101,10 +101,10 @@
}
''');
var parameter = findNode.fieldFormalParameter('void this.f(int i)');
- var typeName = planner.passThrough(parameter);
+ var typeName = planner!.passThrough(parameter);
checkPlan(
- planner.acceptSuffixHint(
- typeName, getPostfixHint(parameter.parameters.rightParenthesis)),
+ planner!.acceptSuffixHint(
+ typeName, getPostfixHint(parameter.parameters!.rightParenthesis)!),
'''
class C {
void Function(int) f;
@@ -116,10 +116,10 @@
Future<void> test_acceptNullabilityHint_function_typed_parameter() async {
await analyze('f(void g(int i) /*?*/) {}');
var parameter = findNode.functionTypedFormalParameter('void g(int i)');
- var typeName = planner.passThrough(parameter);
+ var typeName = planner!.passThrough(parameter);
checkPlan(
- planner.acceptSuffixHint(
- typeName, getPostfixHint(parameter.parameters.rightParenthesis)),
+ planner!.acceptSuffixHint(
+ typeName, getPostfixHint(parameter.parameters.rightParenthesis)!),
'f(void g(int i)?) {}');
}
@@ -127,8 +127,9 @@
var code = 'int /*?*/ x = 0;';
await analyze(code);
var intRef = findNode.simple('int');
- var typeName = planner.passThrough(intRef);
- checkPlan(planner.acceptSuffixHint(typeName, getPostfixHint(intRef.token)),
+ var typeName = planner!.passThrough(intRef);
+ checkPlan(
+ planner!.acceptSuffixHint(typeName, getPostfixHint(intRef.token)!),
'int? x = 0;');
}
@@ -137,10 +138,10 @@
await analyze(code);
var xRef = findNode.simple('x /*');
checkPlan(
- planner.extract(
- xRef.parent.parent,
- planner.acceptSuffixHint(
- planner.passThrough(xRef), getPostfixHint(xRef.token))),
+ planner!.extract(
+ xRef.parent!.parent,
+ planner!.acceptSuffixHint(
+ planner!.passThrough(xRef), getPostfixHint(xRef.token)!)),
'f(x) => x!;');
}
@@ -151,8 +152,8 @@
// *doesn't* produce `a = b = c`, which would be grammatical but which would
// break apart the subexpression `a = b`.
checkPlan(
- planner.addBinaryPostfix(
- planner.passThrough(findNode.assignment('a = b')),
+ planner!.addBinaryPostfix(
+ planner!.passThrough(findNode.assignment('a = b')),
TokenType.EQ,
'c'),
'_f(a, b, c) => (a = b) = c;');
@@ -161,16 +162,16 @@
Future<void> test_addBinaryPostfix_associative() async {
await analyze('var x = 1 - 2;');
checkPlan(
- planner.addBinaryPostfix(
- planner.passThrough(findNode.binary('-')), TokenType.MINUS, '3'),
+ planner!.addBinaryPostfix(
+ planner!.passThrough(findNode.binary('-')), TokenType.MINUS, '3'),
'var x = 1 - 2 - 3;');
}
Future<void> test_addBinaryPostfix_endsInCascade() async {
await analyze('f(x) => x..y = 1;');
checkPlan(
- planner.addBinaryPostfix(
- planner.passThrough(findNode.integerLiteral('1')),
+ planner!.addBinaryPostfix(
+ planner!.passThrough(findNode.integerLiteral('1')),
TokenType.PLUS,
'2'),
'f(x) => x..y = 1 + 2;');
@@ -179,34 +180,34 @@
Future<void> test_addBinaryPostfix_equality_non_associative() async {
await analyze('var x = 1 == 2;');
checkPlan(
- planner.addBinaryPostfix(
- planner.passThrough(findNode.binary('==')), TokenType.EQ_EQ, '3'),
+ planner!.addBinaryPostfix(
+ planner!.passThrough(findNode.binary('==')), TokenType.EQ_EQ, '3'),
'var x = (1 == 2) == 3;');
}
Future<void> test_addBinaryPostfix_inner_precedence() async {
await analyze('var x = 1 < 2;');
checkPlan(
- planner.addBinaryPostfix(
- planner.passThrough(findNode.binary('<')), TokenType.EQ_EQ, 'true'),
+ planner!.addBinaryPostfix(planner!.passThrough(findNode.binary('<')),
+ TokenType.EQ_EQ, 'true'),
'var x = 1 < 2 == true;');
checkPlan(
- planner.addBinaryPostfix(
- planner.passThrough(findNode.binary('<')), TokenType.AS, 'bool'),
+ planner!.addBinaryPostfix(
+ planner!.passThrough(findNode.binary('<')), TokenType.AS, 'bool'),
'var x = (1 < 2) as bool;');
}
Future<void> test_addBinaryPostfix_outer_precedence() async {
await analyze('var x = 1 == true;');
checkPlan(
- planner.addBinaryPostfix(
- planner.passThrough(findNode.integerLiteral('1')),
+ planner!.addBinaryPostfix(
+ planner!.passThrough(findNode.integerLiteral('1')),
TokenType.LT,
'2'),
'var x = 1 < 2 == true;');
checkPlan(
- planner.addBinaryPostfix(
- planner.passThrough(findNode.integerLiteral('1')),
+ planner!.addBinaryPostfix(
+ planner!.passThrough(findNode.integerLiteral('1')),
TokenType.EQ_EQ,
'2'),
'var x = (1 == 2) == true;');
@@ -215,8 +216,8 @@
Future<void> test_addBinaryPostfix_to_expression_function() async {
await analyze('var x = () => null;');
checkPlan(
- planner.addBinaryPostfix(
- planner.passThrough(findNode.functionExpression('()')),
+ planner!.addBinaryPostfix(
+ planner!.passThrough(findNode.functionExpression('()')),
TokenType.AS,
'Object'),
'var x = (() => null) as Object;');
@@ -225,12 +226,12 @@
Future<void> test_addBinaryPrefix_allowCascade() async {
await analyze('f(x) => 1..isEven;');
checkPlan(
- planner.addBinaryPrefix(
- 'x..y', TokenType.EQ, planner.passThrough(findNode.cascade('..'))),
+ planner!.addBinaryPrefix(
+ 'x..y', TokenType.EQ, planner!.passThrough(findNode.cascade('..'))),
'f(x) => x..y = (1..isEven);');
checkPlan(
- planner.addBinaryPrefix(
- 'x', TokenType.EQ, planner.passThrough(findNode.cascade('..')),
+ planner!.addBinaryPrefix(
+ 'x', TokenType.EQ, planner!.passThrough(findNode.cascade('..')),
allowCascade: true),
'f(x) => x = 1..isEven;');
}
@@ -238,76 +239,76 @@
Future<void> test_addBinaryPrefix_assignment_right_associative() async {
await analyze('_f(a, b, c) => b = c;');
checkPlan(
- planner.addBinaryPrefix('a', TokenType.EQ,
- planner.passThrough(findNode.assignment('b = c'))),
+ planner!.addBinaryPrefix('a', TokenType.EQ,
+ planner!.passThrough(findNode.assignment('b = c'))),
'_f(a, b, c) => a = b = c;');
}
Future<void> test_addBinaryPrefix_associative() async {
await analyze('var x = 1 - 2;');
checkPlan(
- planner.addBinaryPrefix(
- '0', TokenType.MINUS, planner.passThrough(findNode.binary('-'))),
+ planner!.addBinaryPrefix(
+ '0', TokenType.MINUS, planner!.passThrough(findNode.binary('-'))),
'var x = 0 - (1 - 2);');
}
Future<void> test_addBinaryPrefix_outer_precedence() async {
await analyze('var x = 2 == true;');
checkPlan(
- planner.addBinaryPrefix('1', TokenType.LT,
- planner.passThrough(findNode.integerLiteral('2'))),
+ planner!.addBinaryPrefix('1', TokenType.LT,
+ planner!.passThrough(findNode.integerLiteral('2'))),
'var x = 1 < 2 == true;');
checkPlan(
- planner.addBinaryPrefix('1', TokenType.EQ_EQ,
- planner.passThrough(findNode.integerLiteral('2'))),
+ planner!.addBinaryPrefix('1', TokenType.EQ_EQ,
+ planner!.passThrough(findNode.integerLiteral('2'))),
'var x = (1 == 2) == true;');
}
Future<void> test_addBinaryPrefix_to_expression_function() async {
await analyze('f(x) => () => null;');
checkPlan(
- planner.addBinaryPrefix('x', TokenType.EQ,
- planner.passThrough(findNode.functionExpression('()'))),
+ planner!.addBinaryPrefix('x', TokenType.EQ,
+ planner!.passThrough(findNode.functionExpression('()'))),
'f(x) => x = () => null;');
}
Future<void> test_addCommentPostfix_before_closer() async {
await analyze('f(g) => g(0);');
checkPlan(
- planner.addCommentPostfix(
- planner.passThrough(findNode.integerLiteral('0')), '/* zero */'),
+ planner!.addCommentPostfix(
+ planner!.passThrough(findNode.integerLiteral('0')), '/* zero */'),
'f(g) => g(0 /* zero */);');
}
Future<void> test_addCommentPostfix_before_other() async {
await analyze('f() => 0.isEven;');
checkPlan(
- planner.addCommentPostfix(
- planner.passThrough(findNode.integerLiteral('0')), '/* zero */'),
+ planner!.addCommentPostfix(
+ planner!.passThrough(findNode.integerLiteral('0')), '/* zero */'),
'f() => 0 /* zero */ .isEven;');
}
Future<void> test_addCommentPostfix_before_semicolon() async {
await analyze('f() => 0;');
checkPlan(
- planner.addCommentPostfix(
- planner.passThrough(findNode.integerLiteral('0')), '/* zero */'),
+ planner!.addCommentPostfix(
+ planner!.passThrough(findNode.integerLiteral('0')), '/* zero */'),
'f() => 0 /* zero */;');
}
Future<void> test_addCommentPostfix_before_space() async {
await analyze('f() => 0 + 1;');
checkPlan(
- planner.addCommentPostfix(
- planner.passThrough(findNode.integerLiteral('0')), '/* zero */'),
+ planner!.addCommentPostfix(
+ planner!.passThrough(findNode.integerLiteral('0')), '/* zero */'),
'f() => 0 /* zero */ + 1;');
}
Future<void> test_addCommentPostfix_informative() async {
await analyze('f() => 0.isEven;');
checkPlan(
- planner.addCommentPostfix(
- planner.passThrough(findNode.integerLiteral('0')), '/* zero */',
+ planner!.addCommentPostfix(
+ planner!.passThrough(findNode.integerLiteral('0')), '/* zero */',
isInformative: true),
'f() => 0.isEven;',
expectedIncludingInformative: 'f() => 0 /* zero */ .isEven;');
@@ -316,47 +317,48 @@
Future<void> test_addPostfix_inner_precedence_add_parens() async {
await analyze('f(x) => -x;');
checkPlan(
- planner.addPostfix(
- planner.passThrough(findNode.prefix('-x')), '.abs()'),
+ planner!
+ .addPostfix(planner!.passThrough(findNode.prefix('-x')), '.abs()'),
'f(x) => (-x).abs();');
}
Future<void> test_addPostfix_inner_precedence_no_parens() async {
await analyze('f(x) => x++;');
checkPlan(
- planner.addPostfix(
- planner.passThrough(findNode.postfix('x++')), '.abs()'),
+ planner!.addPostfix(
+ planner!.passThrough(findNode.postfix('x++')), '.abs()'),
'f(x) => x++.abs();');
}
Future<void> test_addPostfix_outer_precedence() async {
await analyze('f(x) => x/*!*/;');
checkPlan(
- planner.addPostfix(
- planner.passThrough(findNode.simple('x/*!*/')), '.abs()'),
+ planner!.addPostfix(
+ planner!.passThrough(findNode.simple('x/*!*/')), '.abs()'),
'f(x) => x.abs()/*!*/;');
}
Future<void> test_addUnaryPostfix_inner_precedence_add_parens() async {
await analyze('f(x) => -x;');
checkPlan(
- planner.addUnaryPostfix(
- planner.passThrough(findNode.prefix('-x')), TokenType.BANG),
+ planner!.addUnaryPostfix(
+ planner!.passThrough(findNode.prefix('-x')), TokenType.BANG),
'f(x) => (-x)!;');
}
Future<void> test_addUnaryPostfix_inner_precedence_no_parens() async {
await analyze('f(x) => x++;');
checkPlan(
- planner.addUnaryPostfix(
- planner.passThrough(findNode.postfix('x++')), TokenType.BANG),
+ planner!.addUnaryPostfix(
+ planner!.passThrough(findNode.postfix('x++')), TokenType.BANG),
'f(x) => x++!;');
}
Future<void> test_addUnaryPostfix_outer_precedence() async {
await analyze('f(x) => x/*!*/;');
checkPlan(
- planner.addUnaryPostfix(planner.passThrough(findNode.simple('x/*!*/')),
+ planner!.addUnaryPostfix(
+ planner!.passThrough(findNode.simple('x/*!*/')),
TokenType.PLUS_PLUS),
'f(x) => x++/*!*/;');
}
@@ -364,8 +366,8 @@
Future<void> test_addUnaryPrefix_inner_precedence_add_parens() async {
await analyze('f(x, y) => x * y;');
checkPlan(
- planner.addUnaryPrefix(
- TokenType.MINUS, planner.passThrough(findNode.binary('*'))),
+ planner!.addUnaryPrefix(
+ TokenType.MINUS, planner!.passThrough(findNode.binary('*'))),
'f(x, y) => -(x * y);');
}
@@ -375,16 +377,16 @@
// scan as a single `--` token, so we would need parens. Add support for
// this corner case.
checkPlan(
- planner.addUnaryPrefix(
- TokenType.TILDE, planner.passThrough(findNode.prefix('-x'))),
+ planner!.addUnaryPrefix(
+ TokenType.TILDE, planner!.passThrough(findNode.prefix('-x'))),
'f(x) => ~-x;');
}
Future<void> test_addUnaryPrefix_outer_precedence_add_parens() async {
await analyze('f(x) => x!;');
checkPlan(
- planner.addUnaryPrefix(
- TokenType.MINUS, planner.passThrough(findNode.simple('x!'))),
+ planner!.addUnaryPrefix(
+ TokenType.MINUS, planner!.passThrough(findNode.simple('x!'))),
'f(x) => (-x)!;');
}
@@ -394,8 +396,8 @@
// scan as a single `--` token, so we would need parens. Add support for
// this corner case.
checkPlan(
- planner.addUnaryPrefix(
- TokenType.TILDE, planner.passThrough(findNode.simple('x;'))),
+ planner!.addUnaryPrefix(
+ TokenType.TILDE, planner!.passThrough(findNode.simple('x;'))),
'f(x) => -~x;');
}
@@ -413,26 +415,26 @@
assert(identical(innerAssignment, one.parent));
// The tests below will be based on an inner plan that adds `..isEven` after
// the `1`.
- EditPlan makeInnerPlan() => planner.surround(planner.passThrough(one),
+ EditPlan makeInnerPlan() => planner!.surround(planner!.passThrough(one),
suffix: [AtomicEdit.insert('..isEven')], endsInCascade: true);
{
// If we make a plan that passes through `c = 1`, containing a plan that
// adds `..isEven` to `1`, then we don't necessarily want to add parens yet,
// because we might not keep the cascade section above it.
var plan =
- planner.passThrough(innerAssignment, innerPlans: [makeInnerPlan()]);
+ planner!.passThrough(innerAssignment, innerPlans: [makeInnerPlan()]);
// `endsInCascade` returns true because we haven't committed to adding
// parens, so we need to remember that the presence of `..isEven` may
// require parens later.
expect(plan.endsInCascade, true);
- checkPlan(planner.extract(cascade, plan), 'f(a, c) => c = 1..isEven;');
+ checkPlan(planner!.extract(cascade, plan), 'f(a, c) => c = 1..isEven;');
}
{
// If we make a plan that passes through `..b = c = 1`, containing a plan
// that adds `..isEven` to `1`, then we do necessarily want to add parens,
// because we're committed to keeping the cascade section.
var plan =
- planner.passThrough(outerAssignment, innerPlans: [makeInnerPlan()]);
+ planner!.passThrough(outerAssignment, innerPlans: [makeInnerPlan()]);
// We can tell that the parens have been finalized because `endsInCascade`
// returns false now.
expect(plan.endsInCascade, false);
@@ -444,9 +446,9 @@
var code = 'int /*!*/ x = 0;';
await analyze(code);
var intRef = findNode.simple('int');
- var typeName = planner.passThrough(intRef);
+ var typeName = planner!.passThrough(intRef);
checkPlan(
- planner.dropNullabilityHint(typeName, getPostfixHint(intRef.token)),
+ planner!.dropNullabilityHint(typeName, getPostfixHint(intRef.token)!),
'int x = 0;');
}
@@ -454,9 +456,9 @@
var code = 'int /*!*/x = 0;';
await analyze(code);
var intRef = findNode.simple('int');
- var typeName = planner.passThrough(intRef);
+ var typeName = planner!.passThrough(intRef);
var changes = checkPlan(
- planner.dropNullabilityHint(typeName, getPostfixHint(intRef.token)),
+ planner!.dropNullabilityHint(typeName, getPostfixHint(intRef.token)!),
'int x = 0;');
expect(changes.keys, unorderedEquals([code.indexOf('/*')]));
}
@@ -465,9 +467,9 @@
var code = 'int/*!*/x = 0;';
await analyze(code);
var intRef = findNode.simple('int');
- var typeName = planner.passThrough(intRef);
+ var typeName = planner!.passThrough(intRef);
checkPlan(
- planner.dropNullabilityHint(typeName, getPostfixHint(intRef.token)),
+ planner!.dropNullabilityHint(typeName, getPostfixHint(intRef.token)!),
'int x = 0;');
}
@@ -477,18 +479,18 @@
var code = 'void Function()/*!*/x = () {};';
await analyze(code);
var functionType = findNode.genericFunctionType('Function');
- var typeName = planner.passThrough(functionType);
+ var typeName = planner!.passThrough(functionType);
checkPlan(
- planner.dropNullabilityHint(
- typeName, getPostfixHint(functionType.endToken)),
+ planner!.dropNullabilityHint(
+ typeName, getPostfixHint(functionType.endToken)!),
'void Function()x = () {};');
}
Future<void> test_explainNonNullable() async {
await analyze('int x = 0;');
checkPlan(
- planner.explainNonNullable(
- planner.passThrough(findNode.typeAnnotation('int'))),
+ planner!.explainNonNullable(
+ planner!.passThrough(findNode.typeAnnotation('int'))),
'int x = 0;',
expectedIncludingInformative: 'int x = 0;');
}
@@ -583,7 +585,7 @@
// compilation unit is an AstNode with no parent).
await analyze('var x = 0;');
checkPlan(
- planner.surround(planner.passThrough(testUnit),
+ planner!.surround(planner!.passThrough(testUnit),
suffix: [AtomicEdit.insert(' var y = 0;')]),
'var x = 0; var y = 0;');
}
@@ -593,25 +595,25 @@
var sum = findNode.binary('+');
var info = _MockInfo();
var changes = checkPlan(
- planner.passThrough(sum, innerPlans: [
- planner.informativeMessageForToken(sum, sum.operator, info: info)
+ planner!.passThrough(sum, innerPlans: [
+ planner!.informativeMessageForToken(sum, sum.operator, info: info)
]),
'f(x) => x + 1;',
expectedIncludingInformative: 'f(x) => x 1;');
var expectedOffset = sum.operator.offset;
expect(changes.keys, unorderedEquals([expectedOffset]));
expect(changes[expectedOffset], hasLength(1));
- expect(changes[expectedOffset][0].length, '+'.length);
- expect(changes[expectedOffset][0].replacement, '');
- expect(changes[expectedOffset][0].isInformative, isTrue);
- expect(changes[expectedOffset][0].info, same(info));
+ expect(changes[expectedOffset]![0].length, '+'.length);
+ expect(changes[expectedOffset]![0].replacement, '');
+ expect(changes[expectedOffset]![0].isInformative, isTrue);
+ expect(changes[expectedOffset]![0].info, same(info));
}
Future<void> test_insertText() async {
await analyze('final x = 1;');
var variableDeclarationList = findNode.variableDeclarationList('final');
checkPlan(
- planner.insertText(
+ planner!.insertText(
variableDeclarationList,
variableDeclarationList.variables.first.offset,
[AtomicEdit.insert('int ')]),
@@ -621,8 +623,8 @@
Future<void> test_makeNullable() async {
await analyze('int x = 0;');
checkPlan(
- planner
- .makeNullable(planner.passThrough(findNode.typeAnnotation('int'))),
+ planner!
+ .makeNullable(planner!.passThrough(findNode.typeAnnotation('int'))),
'int? x = 0;');
}
@@ -636,8 +638,8 @@
};
}
''');
- var innerPlan = planner.removeNode(findNode.statement('2'));
- var outerPlan = planner.passThrough(findNode.variableDeclaration('x'),
+ var innerPlan = planner!.removeNode(findNode.statement('2'));
+ var outerPlan = planner!.passThrough(findNode.variableDeclaration('x'),
innerPlans: [innerPlan]);
checkPlan(outerPlan, '''
void f() {
@@ -654,15 +656,16 @@
var i1 = findNode.integerLiteral('1');
var i2 = findNode.integerLiteral('2');
checkPlan(
- planner.passThrough(i1.parent,
- innerPlans: [planner.removeNode(i1), planner.removeNode(i2)]),
+ planner!.passThrough(i1.parent,
+ innerPlans: [planner!.removeNode(i1), planner!.removeNode(i2)]),
'var x = [];');
}
Future<void> test_remove_argument() async {
await analyze('f(dynamic d) => d(1, 2, 3);');
var i2 = findNode.integerLiteral('2');
- var changes = checkPlan(planner.removeNode(i2), 'f(dynamic d) => d(1, 3);');
+ var changes =
+ checkPlan(planner!.removeNode(i2), 'f(dynamic d) => d(1, 3);');
expect(changes.keys, [i2.offset]);
}
@@ -675,7 +678,7 @@
}
''');
var declaration = findNode.fieldDeclaration('y');
- var changes = checkPlan(planner.removeNode(declaration), '''
+ var changes = checkPlan(planner!.removeNode(declaration), '''
class C {
int? x;
int? z;
@@ -690,8 +693,8 @@
var i2 = findNode.integerLiteral('2');
var i3 = findNode.integerLiteral('3');
checkPlan(
- planner.passThrough(testUnit,
- innerPlans: [planner.removeNode(i2), planner.removeNode(i3)]),
+ planner!.passThrough(testUnit,
+ innerPlans: [planner!.removeNode(i2), planner!.removeNode(i3)]),
'var x = [[1], 4];');
}
@@ -701,8 +704,8 @@
var i2 = findNode.integerLiteral('2');
var i3 = findNode.integerLiteral('3');
checkPlan(
- planner.passThrough(i2.parent.parent,
- innerPlans: [planner.removeNode(i2), planner.removeNode(i3)]),
+ planner!.passThrough(i2.parent!.parent,
+ innerPlans: [planner!.removeNode(i2), planner!.removeNode(i3)]),
'var x = [[1], [4]];');
}
@@ -711,8 +714,8 @@
var i2 = findNode.integerLiteral('2');
var i3 = findNode.integerLiteral('3');
checkPlan(
- planner.passThrough(testUnit,
- innerPlans: [planner.removeNode(i2), planner.removeNode(i3)]),
+ planner!.passThrough(testUnit,
+ innerPlans: [planner!.removeNode(i2), planner!.removeNode(i3)]),
'var x = [[1], [4]];');
}
@@ -724,8 +727,8 @@
C
}
''');
- var enumConstant = findNode.simple('B').parent;
- var changes = checkPlan(planner.removeNode(enumConstant), '''
+ var enumConstant = findNode.simple('B').parent!;
+ var changes = checkPlan(planner!.removeNode(enumConstant), '''
enum E {
A,
C
@@ -740,8 +743,8 @@
int? x, y, z;
}
''');
- var declaration = findNode.simple('y').parent;
- var changes = checkPlan(planner.removeNode(declaration), '''
+ var declaration = findNode.simple('y').parent!;
+ var changes = checkPlan(planner!.removeNode(declaration), '''
class C {
int? x, z;
}
@@ -752,7 +755,7 @@
Future<void> test_remove_list_element() async {
await analyze('var x = [1, 2, 3];');
var i2 = findNode.integerLiteral('2');
- var changes = checkPlan(planner.removeNode(i2), 'var x = [1, 3];');
+ var changes = checkPlan(planner!.removeNode(i2), 'var x = [1, 3];');
expect(changes.keys, [i2.offset]);
}
@@ -760,20 +763,20 @@
await analyze('var x = [1, 2, 3];');
var i2 = findNode.integerLiteral('2');
var i3 = findNode.integerLiteral('3');
- var changes = checkPlan(planner.removeNode(i3), 'var x = [1, 2];');
+ var changes = checkPlan(planner!.removeNode(i3), 'var x = [1, 2];');
expect(changes.keys, [i2.end]);
}
Future<void> test_remove_list_element_singleton() async {
await analyze('var x = [1];');
var i1 = findNode.integerLiteral('1');
- checkPlan(planner.removeNode(i1), 'var x = [];');
+ checkPlan(planner!.removeNode(i1), 'var x = [];');
}
Future<void> test_remove_list_element_with_trailing_separator() async {
await analyze('var x = [1, 2, 3, ];');
var i3 = findNode.integerLiteral('3');
- checkPlan(planner.removeNode(i3), 'var x = [1, 2, ];');
+ checkPlan(planner!.removeNode(i3), 'var x = [1, 2, ];');
}
Future<void> test_remove_list_elements() async {
@@ -781,8 +784,8 @@
var i2 = findNode.integerLiteral('2');
var i4 = findNode.integerLiteral('4');
var changes = checkPlan(
- planner.passThrough(i2.parent,
- innerPlans: [planner.removeNode(i2), planner.removeNode(i4)]),
+ planner!.passThrough(i2.parent,
+ innerPlans: [planner!.removeNode(i2), planner!.removeNode(i4)]),
'var x = [1, 3, 5];');
expect(changes.keys, unorderedEquals([i2.offset, i4.offset]));
}
@@ -792,8 +795,8 @@
var i1 = findNode.integerLiteral('1');
var i2 = findNode.integerLiteral('2');
checkPlan(
- planner.passThrough(i1.parent,
- innerPlans: [planner.removeNode(i1), planner.removeNode(i2)]),
+ planner!.passThrough(i1.parent,
+ innerPlans: [planner!.removeNode(i1), planner!.removeNode(i2)]),
'var x = [];');
}
@@ -801,7 +804,7 @@
await analyze('var x = [1, 2];');
var i1 = findNode.integerLiteral('1');
var i2 = findNode.integerLiteral('2');
- checkPlan(planner.removeNodes(i1, i2), 'var x = [];');
+ checkPlan(planner!.removeNodes(i1, i2), 'var x = [];');
}
Future<void> test_remove_list_elements_all_passThrough_unit() async {
@@ -809,8 +812,8 @@
var i1 = findNode.integerLiteral('1');
var i2 = findNode.integerLiteral('2');
checkPlan(
- planner.passThrough(testUnit,
- innerPlans: [planner.removeNode(i1), planner.removeNode(i2)]),
+ planner!.passThrough(testUnit,
+ innerPlans: [planner!.removeNode(i1), planner!.removeNode(i2)]),
'var x = [];');
}
@@ -820,8 +823,8 @@
var i2 = findNode.integerLiteral('2');
var i3 = findNode.integerLiteral('3');
var changes = checkPlan(
- planner.passThrough(i2.parent,
- innerPlans: [planner.removeNode(i2), planner.removeNode(i3)]),
+ planner!.passThrough(i2.parent,
+ innerPlans: [planner!.removeNode(i2), planner!.removeNode(i3)]),
'var x = [1];');
expect(changes.keys, unorderedEquals([i1.end, i2.end]));
}
@@ -831,7 +834,7 @@
var i1 = findNode.integerLiteral('1');
var i2 = findNode.integerLiteral('2');
var i3 = findNode.integerLiteral('3');
- var changes = checkPlan(planner.removeNodes(i2, i3), 'var x = [1];');
+ var changes = checkPlan(planner!.removeNodes(i2, i3), 'var x = [1];');
expect(changes.keys, [i1.end]);
}
@@ -839,7 +842,7 @@
await analyze('var x = [1, 2, 3, 4];');
var i2 = findNode.integerLiteral('2');
var i3 = findNode.integerLiteral('3');
- var changes = checkPlan(planner.removeNodes(i2, i3), 'var x = [1, 4];');
+ var changes = checkPlan(planner!.removeNodes(i2, i3), 'var x = [1, 4];');
expect(changes.keys, [i2.offset]);
}
@@ -850,8 +853,8 @@
var i3 = findNode.integerLiteral('3');
createPlanner(removeViaComments: true);
checkPlan(
- planner.passThrough(i2.parent,
- innerPlans: [planner.removeNode(i2), planner.removeNode(i3)]),
+ planner!.passThrough(i2.parent,
+ innerPlans: [planner!.removeNode(i2), planner!.removeNode(i3)]),
'var x = [1/* , 2 *//* , 3 */];');
}
@@ -861,8 +864,8 @@
var i3 = findNode.integerLiteral('3');
createPlanner(removeViaComments: true);
checkPlan(
- planner.passThrough(i2.parent,
- innerPlans: [planner.removeNode(i2), planner.removeNode(i3)]),
+ planner!.passThrough(i2.parent,
+ innerPlans: [planner!.removeNode(i2), planner!.removeNode(i3)]),
'var x = [1, /* 2, */ /* 3, */ 4];');
}
@@ -872,30 +875,31 @@
var i4 = findNode.integerLiteral('4');
createPlanner(removeViaComments: true);
checkPlan(
- planner.passThrough(i2.parent,
- innerPlans: [planner.removeNode(i2), planner.removeNode(i4)]),
+ planner!.passThrough(i2.parent,
+ innerPlans: [planner!.removeNode(i2), planner!.removeNode(i4)]),
'var x = [1, /* 2, */ 3, /* 4, */ 5];');
}
Future<void> test_remove_map_element() async {
await analyze('var x = {1: 2, 3: 4, 5: 6};');
- var entry = findNode.integerLiteral('3').parent;
- var changes = checkPlan(planner.removeNode(entry), 'var x = {1: 2, 5: 6};');
+ var entry = findNode.integerLiteral('3').parent!;
+ var changes =
+ checkPlan(planner!.removeNode(entry), 'var x = {1: 2, 5: 6};');
expect(changes.keys, [entry.offset]);
}
Future<void> test_remove_parameter() async {
await analyze('f(int x, int y, int z) => null;');
- var parameter = findNode.simple('y').parent;
+ var parameter = findNode.simple('y').parent!;
var changes =
- checkPlan(planner.removeNode(parameter), 'f(int x, int z) => null;');
+ checkPlan(planner!.removeNode(parameter), 'f(int x, int z) => null;');
expect(changes.keys, [parameter.offset]);
}
Future<void> test_remove_set_element() async {
await analyze('var x = {1, 2, 3};');
var i2 = findNode.integerLiteral('2');
- var changes = checkPlan(planner.removeNode(i2), 'var x = {1, 3};');
+ var changes = checkPlan(planner!.removeNode(i2), 'var x = {1, 3};');
expect(changes.keys, [i2.offset]);
}
@@ -907,7 +911,7 @@
3;
}
''');
- checkPlan(planner.removeNode(findNode.statement('2')), '''
+ checkPlan(planner!.removeNode(findNode.statement('2')), '''
void f() {
1;
3;
@@ -923,7 +927,7 @@
4;
}
''');
- checkPlan(planner.removeNode(findNode.statement('2')), '''
+ checkPlan(planner!.removeNode(findNode.statement('2')), '''
void f() {
1;
3;
@@ -940,7 +944,7 @@
4;
}
''');
- checkPlan(planner.removeNode(findNode.statement('3')), '''
+ checkPlan(planner!.removeNode(findNode.statement('3')), '''
void f() {
1;
2;
@@ -957,7 +961,7 @@
5;
}
''');
- checkPlan(planner.removeNode(findNode.statement('3')), '''
+ checkPlan(planner!.removeNode(findNode.statement('3')), '''
void f() {
1;
2; 4;
@@ -975,7 +979,7 @@
}
''');
createPlanner(removeViaComments: true);
- checkPlan(planner.removeNode(findNode.statement('2')), '''
+ checkPlan(planner!.removeNode(findNode.statement('2')), '''
void f() {
1;
/* 2; */
@@ -996,7 +1000,7 @@
''');
var s2 = findNode.statement('2');
var s3 = findNode.statement('3');
- var changes = checkPlan(planner.removeNodes(s2, s3), '''
+ var changes = checkPlan(planner!.removeNodes(s2, s3), '''
void f() {
1;
4;
@@ -1021,10 +1025,10 @@
var s3 = findNode.statement('3');
var s4 = findNode.statement('4');
var changes = checkPlan(
- planner.passThrough(s2.parent, innerPlans: [
- planner.removeNode(s2),
- planner.removeNode(s3),
- planner.removeNode(s4)
+ planner!.passThrough(s2.parent, innerPlans: [
+ planner!.removeNode(s2),
+ planner!.removeNode(s3),
+ planner!.removeNode(s4)
]),
'''
void f() {
@@ -1049,8 +1053,8 @@
var s2 = findNode.statement('2');
var s3 = findNode.statement('3');
var changes = checkPlan(
- planner.passThrough(s2.parent,
- innerPlans: [planner.removeNode(s2), planner.removeNode(s3)]),
+ planner!.passThrough(s2.parent,
+ innerPlans: [planner!.removeNode(s2), planner!.removeNode(s3)]),
'''
void f() {
1;
@@ -1073,8 +1077,8 @@
var s2 = findNode.statement('2');
var s4 = findNode.statement('4');
var changes = checkPlan(
- planner.passThrough(s2.parent,
- innerPlans: [planner.removeNode(s2), planner.removeNode(s4)]),
+ planner!.passThrough(s2.parent,
+ innerPlans: [planner!.removeNode(s2), planner!.removeNode(s4)]),
'''
void f() {
1;
@@ -1091,7 +1095,7 @@
1;
}
''');
- checkPlan(planner.removeNode(findNode.statement('1')), '''
+ checkPlan(planner!.removeNode(findNode.statement('1')), '''
void f() {}
''');
}
@@ -1103,7 +1107,7 @@
// Foo
}
''');
- checkPlan(planner.removeNode(findNode.statement('1')), '''
+ checkPlan(planner!.removeNode(findNode.statement('1')), '''
void f() {
// Foo
}
@@ -1117,7 +1121,7 @@
1;
}
''');
- checkPlan(planner.removeNode(findNode.statement('1')), '''
+ checkPlan(planner!.removeNode(findNode.statement('1')), '''
void f() {
// Foo
}
@@ -1137,8 +1141,8 @@
var s2 = findNode.statement('2');
var s3 = findNode.statement('3');
checkPlan(
- planner.passThrough(s2.parent,
- innerPlans: [planner.removeNode(s2), planner.removeNode(s3)]),
+ planner!.passThrough(s2.parent,
+ innerPlans: [planner!.removeNode(s2), planner!.removeNode(s3)]),
'''
void f() {
1;
@@ -1156,7 +1160,7 @@
class E {}
''');
var declaration = findNode.classDeclaration('D');
- var changes = checkPlan(planner.removeNode(declaration), '''
+ var changes = checkPlan(planner!.removeNode(declaration), '''
class C {}
class E {}
''');
@@ -1170,7 +1174,7 @@
import 'dart:math';
''');
var directive = findNode.import('async');
- var changes = checkPlan(planner.removeNode(directive), '''
+ var changes = checkPlan(planner!.removeNode(directive), '''
import 'dart:io';
import 'dart:math';
''');
@@ -1182,8 +1186,8 @@
class C<T, U, V> {}
C<int, double, String>? c;
''');
- var typeArgument = findNode.simple('double').parent;
- var changes = checkPlan(planner.removeNode(typeArgument), '''
+ var typeArgument = findNode.simple('double').parent!;
+ var changes = checkPlan(planner!.removeNode(typeArgument), '''
class C<T, U, V> {}
C<int, String>? c;
''');
@@ -1192,15 +1196,15 @@
Future<void> test_remove_type_parameter() async {
await analyze('class C<T, U, V> {}');
- var parameter = findNode.simple('U').parent;
- var changes = checkPlan(planner.removeNode(parameter), 'class C<T, V> {}');
+ var parameter = findNode.simple('U').parent!;
+ var changes = checkPlan(planner!.removeNode(parameter), 'class C<T, V> {}');
expect(changes.keys, [parameter.offset]);
}
Future<void> test_remove_variable_declaration() async {
await analyze('int? x, y, z;');
- var declaration = findNode.simple('y').parent;
- var changes = checkPlan(planner.removeNode(declaration), 'int? x, z;');
+ var declaration = findNode.simple('y').parent!;
+ var changes = checkPlan(planner!.removeNode(declaration), 'int? x, z;');
expect(changes.keys, [declaration.offset]);
}
@@ -1209,10 +1213,10 @@
await analyze('f(x) => x?.m(0);');
var methodInvocation = findNode.methodInvocation('?.');
checkPlan(
- planner.passThrough(methodInvocation, innerPlans: [
- planner.removeNullAwareness(methodInvocation),
- planner.passThrough(methodInvocation.argumentList, innerPlans: [
- planner
+ planner!.passThrough(methodInvocation, innerPlans: [
+ planner!.removeNullAwareness(methodInvocation),
+ planner!.passThrough(methodInvocation.argumentList, innerPlans: [
+ planner!
.replace(findNode.integerLiteral('0'), [AtomicEdit.insert('1')])
])
]),
@@ -1224,9 +1228,9 @@
await analyze('f(x) => x?.m();');
var methodInvocation = findNode.methodInvocation('?.');
checkPlan(
- planner.passThrough(methodInvocation, innerPlans: [
- planner.removeNullAwareness(methodInvocation),
- planner.replace(findNode.simple('m'), [AtomicEdit.insert('n')])
+ planner!.passThrough(methodInvocation, innerPlans: [
+ planner!.removeNullAwareness(methodInvocation),
+ planner!.replace(findNode.simple('m'), [AtomicEdit.insert('n')])
]),
'f(x) => x.n();');
}
@@ -1236,9 +1240,9 @@
await analyze('f(x) => x?.m();');
var methodInvocation = findNode.methodInvocation('?.');
checkPlan(
- planner.passThrough(methodInvocation, innerPlans: [
- planner.replace(findNode.simple('x?.'), [AtomicEdit.insert('y')]),
- planner.removeNullAwareness(methodInvocation)
+ planner!.passThrough(methodInvocation, innerPlans: [
+ planner!.replace(findNode.simple('x?.'), [AtomicEdit.insert('y')]),
+ planner!.removeNullAwareness(methodInvocation)
]),
'f(x) => y.m();');
}
@@ -1248,10 +1252,10 @@
await analyze('f(x) => x?.m<int>();');
var methodInvocation = findNode.methodInvocation('?.');
checkPlan(
- planner.passThrough(methodInvocation, innerPlans: [
- planner.removeNullAwareness(methodInvocation),
- planner.passThrough(methodInvocation.typeArguments, innerPlans: [
- planner.replace(findNode.simple('int'), [AtomicEdit.insert('num')])
+ planner!.passThrough(methodInvocation, innerPlans: [
+ planner!.removeNullAwareness(methodInvocation),
+ planner!.passThrough(methodInvocation.typeArguments, innerPlans: [
+ planner!.replace(findNode.simple('int'), [AtomicEdit.insert('num')])
])
]),
'f(x) => x.m<num>();');
@@ -1261,8 +1265,8 @@
await analyze('f(x) => x?.m();');
var methodInvocation = findNode.methodInvocation('?.');
checkPlan(
- planner.passThrough(methodInvocation,
- innerPlans: [planner.removeNullAwareness(methodInvocation)]),
+ planner!.passThrough(methodInvocation,
+ innerPlans: [planner!.removeNullAwareness(methodInvocation)]),
'f(x) => x.m();');
}
@@ -1270,10 +1274,10 @@
await analyze('f(x) => x?.y;');
var propertyAccess = findNode.propertyAccess('?.');
checkPlan(
- planner.passThrough(propertyAccess, innerPlans: [
- (planner.replace(findNode.simple('x?.'), [AtomicEdit.insert('z')])),
- planner.removeNullAwareness(propertyAccess),
- planner.replace(findNode.simple('y'), [AtomicEdit.insert('w')])
+ planner!.passThrough(propertyAccess, innerPlans: [
+ (planner!.replace(findNode.simple('x?.'), [AtomicEdit.insert('z')])),
+ planner!.removeNullAwareness(propertyAccess),
+ planner!.replace(findNode.simple('y'), [AtomicEdit.insert('w')])
]),
'f(x) => z.w;');
}
@@ -1283,9 +1287,9 @@
await analyze('f(x) => x?.y;');
var propertyAccess = findNode.propertyAccess('?.');
checkPlan(
- planner.passThrough(propertyAccess, innerPlans: [
- planner.removeNullAwareness(propertyAccess),
- planner.replace(findNode.simple('y'), [AtomicEdit.insert('w')])
+ planner!.passThrough(propertyAccess, innerPlans: [
+ planner!.removeNullAwareness(propertyAccess),
+ planner!.replace(findNode.simple('y'), [AtomicEdit.insert('w')])
]),
'f(x) => x.w;');
}
@@ -1295,9 +1299,9 @@
await analyze('f(x) => x?.y;');
var propertyAccess = findNode.propertyAccess('?.');
checkPlan(
- planner.passThrough(propertyAccess, innerPlans: [
- planner.replace(findNode.simple('x?.'), [AtomicEdit.insert('z')]),
- planner.removeNullAwareness(propertyAccess)
+ planner!.passThrough(propertyAccess, innerPlans: [
+ planner!.replace(findNode.simple('x?.'), [AtomicEdit.insert('z')]),
+ planner!.removeNullAwareness(propertyAccess)
]),
'f(x) => z.y;');
}
@@ -1306,21 +1310,21 @@
await analyze('f(x) => x?.y;');
var propertyAccess = findNode.propertyAccess('?.');
checkPlan(
- planner.passThrough(propertyAccess,
- innerPlans: [planner.removeNullAwareness(propertyAccess)]),
+ planner!.passThrough(propertyAccess,
+ innerPlans: [planner!.removeNullAwareness(propertyAccess)]),
'f(x) => x.y;');
}
Future<void> test_replace_expression() async {
await analyze('var x = 1 + 2 * 3;');
- checkPlan(planner.replace(findNode.binary('*'), [AtomicEdit.insert('6')]),
+ checkPlan(planner!.replace(findNode.binary('*'), [AtomicEdit.insert('6')]),
'var x = 1 + 6;');
}
Future<void> test_replace_expression_add_parens_due_to_cascade() async {
await analyze('var x = 1 + 2 * 3;');
checkPlan(
- planner.replace(findNode.binary('*'), [AtomicEdit.insert('4..isEven')],
+ planner!.replace(findNode.binary('*'), [AtomicEdit.insert('4..isEven')],
endsInCascade: true),
'var x = 1 + (4..isEven);');
}
@@ -1328,7 +1332,7 @@
Future<void> test_replace_expression_add_parens_due_to_precedence() async {
await analyze('var x = 1 + 2 * 3;');
checkPlan(
- planner.replace(findNode.binary('*'), [AtomicEdit.insert('y = z')],
+ planner!.replace(findNode.binary('*'), [AtomicEdit.insert('y = z')],
precedence: Precedence.assignment),
'var x = 1 + (y = z);');
}
@@ -1337,19 +1341,19 @@
await analyze('var x = 1;');
var variableDeclarationList = findNode.variableDeclarationList('var x');
checkPlan(
- planner.replaceToken(
- variableDeclarationList, variableDeclarationList.keyword, 'int'),
+ planner!.replaceToken(
+ variableDeclarationList, variableDeclarationList.keyword!, 'int'),
'int x = 1;');
}
Future<void> test_surround_allowCascade() async {
await analyze('f(x) => 1..isEven;');
checkPlan(
- planner.surround(planner.passThrough(findNode.cascade('..')),
+ planner!.surround(planner!.passThrough(findNode.cascade('..')),
prefix: [AtomicEdit.insert('x..y = ')]),
'f(x) => x..y = (1..isEven);');
checkPlan(
- planner.surround(planner.passThrough(findNode.cascade('..')),
+ planner!.surround(planner!.passThrough(findNode.cascade('..')),
prefix: [AtomicEdit.insert('x = ')], allowCascade: true),
'f(x) => x = 1..isEven;');
}
@@ -1357,13 +1361,13 @@
Future<void> test_surround_associative() async {
await analyze('var x = 1 - 2;');
checkPlan(
- planner.surround(planner.passThrough(findNode.binary('-')),
+ planner!.surround(planner!.passThrough(findNode.binary('-')),
suffix: [AtomicEdit.insert(' - 3')],
innerPrecedence: Precedence.additive,
associative: true),
'var x = 1 - 2 - 3;');
checkPlan(
- planner.surround(planner.passThrough(findNode.binary('-')),
+ planner!.surround(planner!.passThrough(findNode.binary('-')),
prefix: [AtomicEdit.insert('0 - ')],
innerPrecedence: Precedence.additive),
'var x = 0 - (1 - 2);');
@@ -1372,11 +1376,11 @@
Future<void> test_surround_endsInCascade() async {
await analyze('f(x) => x..y = 1;');
checkPlan(
- planner.surround(planner.passThrough(findNode.integerLiteral('1')),
+ planner!.surround(planner!.passThrough(findNode.integerLiteral('1')),
suffix: [AtomicEdit.insert(' + 2')]),
'f(x) => x..y = 1 + 2;');
checkPlan(
- planner.surround(planner.passThrough(findNode.integerLiteral('1')),
+ planner!.surround(planner!.passThrough(findNode.integerLiteral('1')),
suffix: [AtomicEdit.insert('..isEven')], endsInCascade: true),
'f(x) => x..y = (1..isEven);');
}
@@ -1385,16 +1389,16 @@
test_surround_endsInCascade_does_not_propagate_through_added_parens() async {
await analyze('f(a) => a..b = 0;');
checkPlan(
- planner.surround(
- planner.surround(planner.passThrough(findNode.cascade('..')),
+ planner!.surround(
+ planner!.surround(planner!.passThrough(findNode.cascade('..')),
prefix: [AtomicEdit.insert('1 + ')],
innerPrecedence: Precedence.additive),
prefix: [AtomicEdit.insert('true ? ')],
suffix: [AtomicEdit.insert(' : 2')]),
'f(a) => true ? 1 + (a..b = 0) : 2;');
checkPlan(
- planner.surround(
- planner.surround(planner.passThrough(findNode.cascade('..')),
+ planner!.surround(
+ planner!.surround(planner!.passThrough(findNode.cascade('..')),
prefix: [AtomicEdit.insert('throw ')], allowCascade: true),
prefix: [AtomicEdit.insert('true ? ')],
suffix: [AtomicEdit.insert(' : 2')]),
@@ -1404,7 +1408,7 @@
Future<void> test_surround_endsInCascade_internal_throw() async {
await analyze('f(x, g) => g(0, throw x, 1);');
checkPlan(
- planner.surround(planner.passThrough(findNode.simple('x, 1')),
+ planner!.surround(planner!.passThrough(findNode.simple('x, 1')),
suffix: [AtomicEdit.insert('..y')], endsInCascade: true),
'f(x, g) => g(0, throw x..y, 1);');
}
@@ -1412,16 +1416,18 @@
Future<void> test_surround_endsInCascade_propagates() async {
await analyze('f(a) => a..b = 0;');
checkPlan(
- planner.surround(
- planner.surround(planner.passThrough(findNode.cascade('..')),
+ planner!.surround(
+ planner!.surround(planner!.passThrough(findNode.cascade('..')),
prefix: [AtomicEdit.insert('throw ')], allowCascade: true),
prefix: [AtomicEdit.insert('true ? ')],
suffix: [AtomicEdit.insert(' : 2')]),
'f(a) => true ? (throw a..b = 0) : 2;');
checkPlan(
- planner.surround(
- planner.surround(planner.passThrough(findNode.integerLiteral('0')),
- prefix: [AtomicEdit.insert('throw ')], allowCascade: true),
+ planner!.surround(
+ planner!.surround(
+ planner!.passThrough(findNode.integerLiteral('0')),
+ prefix: [AtomicEdit.insert('throw ')],
+ allowCascade: true),
prefix: [AtomicEdit.insert('true ? ')],
suffix: [AtomicEdit.insert(' : 2')]),
'f(a) => a..b = true ? throw 0 : 2;');
@@ -1430,12 +1436,12 @@
Future<void> test_surround_precedence() async {
await analyze('var x = 1 == true;');
checkPlan(
- planner.surround(planner.passThrough(findNode.integerLiteral('1')),
+ planner!.surround(planner!.passThrough(findNode.integerLiteral('1')),
suffix: [AtomicEdit.insert(' < 2')],
outerPrecedence: Precedence.relational),
'var x = 1 < 2 == true;');
checkPlan(
- planner.surround(planner.passThrough(findNode.integerLiteral('1')),
+ planner!.surround(planner!.passThrough(findNode.integerLiteral('1')),
suffix: [AtomicEdit.insert(' == 2')],
outerPrecedence: Precedence.equality),
'var x = (1 == 2) == true;');
@@ -1444,7 +1450,7 @@
Future<void> test_surround_prefix() async {
await analyze('var x = 1;');
checkPlan(
- planner.surround(planner.passThrough(findNode.integerLiteral('1')),
+ planner!.surround(planner!.passThrough(findNode.integerLiteral('1')),
prefix: [AtomicEdit.insert('throw ')]),
'var x = throw 1;');
}
@@ -1452,7 +1458,7 @@
Future<void> test_surround_suffix() async {
await analyze('var x = 1;');
checkPlan(
- planner.surround(planner.passThrough(findNode.integerLiteral('1')),
+ planner!.surround(planner!.passThrough(findNode.integerLiteral('1')),
suffix: [AtomicEdit.insert('..isEven')]),
'var x = 1..isEven;');
}
@@ -1460,7 +1466,7 @@
Future<void> test_surround_suffix_parenthesized() async {
await analyze('var x = (1);');
checkPlan(
- planner.surround(planner.passThrough(findNode.integerLiteral('1')),
+ planner!.surround(planner!.passThrough(findNode.integerLiteral('1')),
suffix: [AtomicEdit.insert('..isEven')]),
'var x = 1..isEven;');
}
@@ -1468,8 +1474,8 @@
Future<void> test_surround_suffix_parenthesized_passThrough_unit() async {
await analyze('var x = (1);');
checkPlan(
- planner.passThrough(testUnit, innerPlans: [
- planner.surround(planner.passThrough(findNode.integerLiteral('1')),
+ planner!.passThrough(testUnit, innerPlans: [
+ planner!.surround(planner!.passThrough(findNode.integerLiteral('1')),
suffix: [AtomicEdit.insert('..isEven')])
]),
'var x = 1..isEven;');
@@ -1478,12 +1484,12 @@
Future<void> test_surround_threshold() async {
await analyze('var x = 1 < 2;');
checkPlan(
- planner.surround(planner.passThrough(findNode.binary('<')),
+ planner!.surround(planner!.passThrough(findNode.binary('<')),
suffix: [AtomicEdit.insert(' == true')],
innerPrecedence: Precedence.equality),
'var x = 1 < 2 == true;');
checkPlan(
- planner.surround(planner.passThrough(findNode.binary('<')),
+ planner!.surround(planner!.passThrough(findNode.binary('<')),
suffix: [AtomicEdit.insert(' as bool')],
innerPrecedence: Precedence.relational),
'var x = (1 < 2) as bool;');
@@ -1525,7 +1531,7 @@
class PrecedenceTest extends AbstractSingleUnitTest {
Future<void> checkPrecedence(String content) async {
await resolveTestUnit(content);
- testUnit.accept(_PrecedenceChecker(testUnit.lineInfo, testCode));
+ testUnit!.accept(_PrecedenceChecker(testUnit!.lineInfo, testCode));
}
Future<void> test_precedence_as() async {
@@ -1648,7 +1654,8 @@
Future<void> test_precedenceChecker_detects_unnecessary_paren() async {
await resolveTestUnit('var x = (1);');
expect(
- () => testUnit.accept(_PrecedenceChecker(testUnit.lineInfo, testCode)),
+ () =>
+ testUnit!.accept(_PrecedenceChecker(testUnit!.lineInfo, testCode)),
throwsA(TypeMatcher<TestFailure>()));
}
}
@@ -1660,7 +1667,7 @@
class _PrecedenceChecker extends UnifyingAstVisitor<void> {
final EditPlanner planner;
- _PrecedenceChecker(LineInfo lineInfo, String sourceText)
+ _PrecedenceChecker(LineInfo? lineInfo, String? sourceText)
: planner = EditPlanner(lineInfo, sourceText);
@override
diff --git a/pkg/nnbd_migration/test/edit_planner_pass_through_merging_test.dart b/pkg/nnbd_migration/test/edit_planner_pass_through_merging_test.dart
index 1bc5d2a..37bb09b 100644
--- a/pkg/nnbd_migration/test/edit_planner_pass_through_merging_test.dart
+++ b/pkg/nnbd_migration/test/edit_planner_pass_through_merging_test.dart
@@ -22,7 +22,7 @@
class PassThroughMergingTest extends AbstractSingleUnitTest {
Future<void> test_creates_pass_through_plans_stepwise() async {
await resolveTestUnit('var x = [[[1]]];');
- var plan = _EditPlannerForTesting(testCode).passThrough(
+ var plan = _EditPlannerForTesting(testCode!).passThrough(
findNode.listLiteral('[[['),
innerPlans: [_MockPlan(findNode.integerLiteral('1'))]);
expect(plan.toString(),
@@ -31,7 +31,7 @@
Future<void> test_merge_plans_at_lower_level() async {
await resolveTestUnit('var x = [[1, 2]];');
- var plan = _EditPlannerForTesting(testCode)
+ var plan = _EditPlannerForTesting(testCode!)
.passThrough(findNode.listLiteral('[['), innerPlans: [
_MockPlan(findNode.integerLiteral('1')),
_MockPlan(findNode.integerLiteral('2'))
@@ -42,7 +42,7 @@
Future<void> test_merge_plans_at_top_level() async {
await resolveTestUnit('var x = [[1], [2]];');
- var plan = _EditPlannerForTesting(testCode)
+ var plan = _EditPlannerForTesting(testCode!)
.passThrough(findNode.listLiteral('[['), innerPlans: [
_MockPlan(findNode.integerLiteral('1')),
_MockPlan(findNode.integerLiteral('2'))
@@ -53,7 +53,7 @@
Future<void> test_merge_plans_at_varying_levels() async {
await resolveTestUnit('var x = [1, [2, 3], 4];');
- var plan = _EditPlannerForTesting(testCode)
+ var plan = _EditPlannerForTesting(testCode!)
.passThrough(findNode.listLiteral('[1'), innerPlans: [
_MockPlan(findNode.integerLiteral('1')),
_MockPlan(findNode.integerLiteral('2')),
@@ -72,7 +72,7 @@
: super(LineInfo.fromContent(content), content);
@override
- PassThroughBuilder createPassThroughBuilder(AstNode node) =>
+ PassThroughBuilder createPassThroughBuilder(AstNode? node) =>
_MockPassThroughBuilder(node);
}
@@ -80,7 +80,7 @@
final List<EditPlan> _innerPlans = [];
@override
- final AstNode node;
+ final AstNode? node;
_MockPassThroughBuilder(this.node);
@@ -96,7 +96,7 @@
}
class _MockPlan implements NodeProducingEditPlan {
- final AstNode _node;
+ final AstNode? _node;
final List<EditPlan> _innerPlans;
@@ -104,7 +104,7 @@
: _innerPlans = innerPlans;
@override
- AstNode get parentNode => _node.parent;
+ AstNode? get parentNode => _node!.parent;
@override
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
diff --git a/pkg/nnbd_migration/test/fix_aggregator_test.dart b/pkg/nnbd_migration/test/fix_aggregator_test.dart
index 4430123..27a9571 100644
--- a/pkg/nnbd_migration/test/fix_aggregator_test.dart
+++ b/pkg/nnbd_migration/test/fix_aggregator_test.dart
@@ -41,8 +41,8 @@
var previewInfo = run({
findNode.unit: NodeChangeForCompilationUnit()
..addImport('package:collection/collection.dart', 'IterableExtension')
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
library foo;
import 'package:collection/collection.dart' show IterableExtension;
@@ -63,8 +63,8 @@
var previewInfo = run({
findNode.unit: NodeChangeForCompilationUnit()
..addImport('package:collection/collection.dart', 'IterableExtension')
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
library foo;
import 'package:collection/collection.dart' show IterableExtension;
@@ -85,8 +85,8 @@
findNode.unit: NodeChangeForCompilationUnit()
..addImport('package:fixnum/fixnum.dart', 'Int32')
..addImport('package:collection/collection.dart', 'IterableExtension')
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
import 'package:args/args.dart';
import 'package:collection/collection.dart' show IterableExtension;
import 'package:fixnum/fixnum.dart' show Int32;
@@ -106,8 +106,8 @@
findNode.unit: NodeChangeForCompilationUnit()
..addImport('package:collection/collection.dart', 'IterableExtension')
..addImport('package:args/args.dart', 'ArgParser')
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
import 'package:args/args.dart' show ArgParser;
import 'package:collection/collection.dart' show IterableExtension;
import 'package:fixnum/fixnum.dart';
@@ -125,8 +125,8 @@
var previewInfo = run({
findNode.unit: NodeChangeForCompilationUnit()
..addImport('package:collection/collection.dart', 'IterableExtension')
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
import 'package:collection/collection.dart' show IterableExtension;
export 'dart:async';
@@ -142,8 +142,8 @@
findNode.unit: NodeChangeForCompilationUnit()
..addImport('dart:async', 'Future')
..addImport('dart:math', 'sin')
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
import 'dart:async' show Future;
import 'dart:math' show sin;
@@ -167,8 +167,8 @@
NodeChangeForShowCombinator()..addName('Int64'),
findNode.expression('null'): NodeChangeForExpression()
..addExpressionChange(NullCheckChange(MockDartType()), null)
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
import 'package:args/args.dart';
import 'package:collection/collection.dart' show IterableExtension;
import 'package:fixnum/fixnum.dart' show Int32, Int64;
@@ -185,8 +185,8 @@
findNode.unit: NodeChangeForCompilationUnit()
..addImport('dart:async', 'Stream')
..addImport('dart:async', 'Future')
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
import 'dart:async' show Future, Stream;
main() {}
@@ -205,8 +205,8 @@
var previewInfo = run({
findNode.unit: NodeChangeForCompilationUnit()
..addImport('package:collection/collection.dart', 'IterableExtension')
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
import 'package:args/args.dart';
import 'package:collection/collection.dart' show IterableExtension;
import 'package:fixnum/fixnum.dart';
@@ -226,8 +226,8 @@
findNode.unit: NodeChangeForCompilationUnit()
..addImport('package:fixnum/fixnum.dart', 'Int32')
..addImport('package:args/args.dart', 'ArgParser')
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
import 'package:args/args.dart' show ArgParser;
import 'package:collection/collection.dart';
import 'package:fixnum/fixnum.dart' show Int32;
@@ -241,8 +241,8 @@
var previewInfo = run({
findNode.defaultParameter('int x'): NodeChangeForDefaultFormalParameter()
..addRequiredKeyword = true
- });
- expect(previewInfo.applyTo(code), 'f({required int x}) => 0;');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f({required int x}) => 0;');
}
Future<void> test_addRequired_afterMetadata() async {
@@ -250,8 +250,8 @@
var previewInfo = run({
findNode.defaultParameter('int x'): NodeChangeForDefaultFormalParameter()
..addRequiredKeyword = true
- });
- expect(previewInfo.applyTo(code), 'f({@deprecated required int x}) => 0;');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f({@deprecated required int x}) => 0;');
}
Future<void> test_addRequired_afterMetadata_andRequiredAnnotation() async {
@@ -266,17 +266,17 @@
findNode.defaultParameter('int x'): NodeChangeForDefaultFormalParameter()
..addRequiredKeyword = true
..annotationToRemove = annotation
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
import 'package:meta/meta.dart';
f({@deprecated required int x}) {}
''');
expect(previewInfo.values, hasLength(2));
expect(previewInfo[content.indexOf('int ')], hasLength(1));
- expect(previewInfo[content.indexOf('int ')].single.isInsertion, true);
+ expect(previewInfo[content.indexOf('int ')]!.single.isInsertion, true);
expect(previewInfo[content.indexOf('@required')], isNotNull);
- expect(previewInfo[content.indexOf('@required')].single.isDeletion, true);
+ expect(previewInfo[content.indexOf('@required')]!.single.isDeletion, true);
}
Future<void> test_addRequired_afterMetadata_beforeFinal() async {
@@ -284,8 +284,8 @@
var previewInfo = run({
findNode.defaultParameter('int x'): NodeChangeForDefaultFormalParameter()
..addRequiredKeyword = true
- });
- expect(previewInfo.applyTo(code),
+ })!;
+ expect(previewInfo.applyTo(code!),
'f({@deprecated required final int x}) => 0;');
}
@@ -294,9 +294,9 @@
var previewInfo = run({
findNode.defaultParameter('int x'): NodeChangeForDefaultFormalParameter()
..addRequiredKeyword = true
- });
+ })!;
expect(
- previewInfo.applyTo(code), 'f({@deprecated required int x()}) => 0;');
+ previewInfo.applyTo(code!), 'f({@deprecated required int x()}) => 0;');
}
Future<void> test_addShownName_atEnd_multiple() async {
@@ -305,8 +305,9 @@
findNode.import('dart:math').combinators[0]: NodeChangeForShowCombinator()
..addName('tan')
..addName('sin')
- });
- expect(previewInfo.applyTo(code), "import 'dart:math' show cos, sin, tan;");
+ })!;
+ expect(
+ previewInfo.applyTo(code!), "import 'dart:math' show cos, sin, tan;");
}
Future<void> test_addShownName_atStart_multiple() async {
@@ -315,8 +316,9 @@
findNode.import('dart:math').combinators[0]: NodeChangeForShowCombinator()
..addName('sin')
..addName('cos')
- });
- expect(previewInfo.applyTo(code), "import 'dart:math' show cos, sin, tan;");
+ })!;
+ expect(
+ previewInfo.applyTo(code!), "import 'dart:math' show cos, sin, tan;");
}
Future<void> test_addShownName_sorted() async {
@@ -324,8 +326,9 @@
var previewInfo = run({
findNode.import('dart:math').combinators[0]: NodeChangeForShowCombinator()
..addName('sin')
- });
- expect(previewInfo.applyTo(code), "import 'dart:math' show cos, sin, tan;");
+ })!;
+ expect(
+ previewInfo.applyTo(code!), "import 'dart:math' show cos, sin, tan;");
}
Future<void> test_addShownName_sorted_multiple() async {
@@ -334,8 +337,9 @@
findNode.import('dart:math').combinators[0]: NodeChangeForShowCombinator()
..addName('tan')
..addName('cos')
- });
- expect(previewInfo.applyTo(code), "import 'dart:math' show cos, sin, tan;");
+ })!;
+ expect(
+ previewInfo.applyTo(code!), "import 'dart:math' show cos, sin, tan;");
}
Future<void> test_adjacentFixes() async {
@@ -349,8 +353,8 @@
..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo()),
findNode.binary('a + b'): NodeChangeForExpression()
..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo())
- });
- expect(previewInfo.applyTo(code), 'f(a, b) => (a! + b!)!;');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f(a, b) => (a! + b!)!;');
}
Future<void> test_argument_list_drop_all_arguments() async {
@@ -363,8 +367,8 @@
findNode.methodInvocation('f(x').argumentList: NodeChangeForArgumentList()
..dropArgument(findNode.simple('y);'), null)
..dropArgument(findNode.simple('x, y'), null)
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
f([int x, int y]) => null;
g(int x, int y) => f();
''');
@@ -379,8 +383,8 @@
var previewInfo = run({
findNode.methodInvocation('f(x').argumentList: NodeChangeForArgumentList()
..dropArgument(findNode.simple('y);'), null)
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
f([int x, int y]) => null;
g(int x, int y) => f(x);
''');
@@ -397,8 +401,8 @@
..dropArgument(findNode.simple('y);'), null),
findNode.simple('x, y'): NodeChangeForExpression()
..addExpressionChange(NullCheckChange(MockDartType()), null)
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
f([int x, int y]) => null;
g(int x, int y) => f(x!);
''');
@@ -410,8 +414,8 @@
var previewInfo = run({
findNode.assignment('+='): NodeChangeForAssignment()
..addExpressionChange(NullCheckChange(MockDartType()), null)
- });
- expect(previewInfo.applyTo(code), 'f(int x, int y) => (x += y)!;');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f(int x, int y) => (x += y)!;');
}
Future<void> test_assignment_change_lhs() async {
@@ -421,8 +425,8 @@
findNode.assignment('+='): NodeChangeForAssignment(),
findNode.index('[0]').target: NodeChangeForExpression()
..addExpressionChange(NullCheckChange(MockDartType()), null)
- });
- expect(previewInfo.applyTo(code), 'f(List<int> x, int y) => x![0] += y;');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f(List<int> x, int y) => x![0] += y;');
}
Future<void> test_assignment_change_rhs() async {
@@ -433,8 +437,8 @@
assignment: NodeChangeForAssignment(),
assignment.rightHandSide: NodeChangeForExpression()
..addExpressionChange(NullCheckChange(MockDartType()), null)
- });
- expect(previewInfo.applyTo(code), 'f(int x, int y) => x += y!;');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f(int x, int y) => x += y!;');
}
Future<void> test_assignment_compound_with_bad_combined_type() async {
@@ -443,11 +447,11 @@
var previewInfo = run({
findNode.assignment('+='): NodeChangeForAssignment()
..hasBadCombinedType = true
- });
- expect(previewInfo.applyTo(code), content);
+ })!;
+ expect(previewInfo.applyTo(code!), content);
expect(previewInfo, hasLength(1));
- var edit = previewInfo[content.indexOf('+=')].single;
- expect(edit.info.description,
+ var edit = previewInfo[content.indexOf('+=')]!.single;
+ expect(edit.info!.description,
NullabilityFixDescription.compoundAssignmentHasBadCombinedType);
expect(edit.isInformative, isTrue);
expect(edit.length, '+='.length);
@@ -459,11 +463,11 @@
var previewInfo = run({
findNode.assignment('+='): NodeChangeForAssignment()
..hasNullableSource = true
- });
- expect(previewInfo.applyTo(code), content);
+ })!;
+ expect(previewInfo.applyTo(code!), content);
expect(previewInfo, hasLength(1));
- var edit = previewInfo[content.indexOf('+=')].single;
- expect(edit.info.description,
+ var edit = previewInfo[content.indexOf('+=')]!.single;
+ expect(edit.info!.description,
NullabilityFixDescription.compoundAssignmentHasNullableSource);
expect(edit.isInformative, isTrue);
expect(edit.length, '+='.length);
@@ -477,8 +481,8 @@
..addExpressionChange(
IntroduceAsChange(nnbdTypeProvider.intType, isDowncast: false),
null)
- });
- expect(previewInfo.applyTo(code), 'f(int x, int y) => (x += y) as int;');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f(int x, int y) => (x += y) as int;');
}
Future<void> test_assignment_weak_null_aware() async {
@@ -487,11 +491,11 @@
var previewInfo = run({
findNode.assignment('??='): NodeChangeForAssignment()
..isWeakNullAware = true
- }, warnOnWeakCode: true);
- expect(previewInfo.applyTo(code), content);
+ }, warnOnWeakCode: true)!;
+ expect(previewInfo.applyTo(code!), content);
expect(previewInfo, hasLength(1));
- var edit = previewInfo[content.indexOf('??=')].single;
- expect(edit.info.description,
+ var edit = previewInfo[content.indexOf('??=')]!.single;
+ expect(edit.info!.description,
NullabilityFixDescription.nullAwareAssignmentUnnecessaryInStrongMode);
expect(edit.isInformative, isTrue);
expect(edit.length, '??='.length);
@@ -503,8 +507,8 @@
var previewInfo = run({
findNode.assignment('??='): NodeChangeForAssignment()
..isWeakNullAware = true
- }, warnOnWeakCode: false);
- expect(previewInfo.applyTo(code), 'f(int x, int y) => x;');
+ }, warnOnWeakCode: false)!;
+ expect(previewInfo.applyTo(code!), 'f(int x, int y) => x;');
}
Future<void> test_eliminateDeadIf_changesInKeptCode() async {
@@ -518,8 +522,8 @@
..conditionValue = true,
findNode.simple('j.isEven'): NodeChangeForExpression()
..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo())
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
f(int i, int/*?*/ j) {
j!.isEven;
}
@@ -539,8 +543,8 @@
..conditionValue = true,
findNode.simple('j.isEven'): NodeChangeForExpression()
..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo())
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
f(int i, int/*?*/ j) {
j!.isEven;
}
@@ -555,8 +559,8 @@
''');
var previewInfo = run({
findNode.ifElement('=='): NodeChangeForIfElement()..conditionValue = false
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
List<int> f(int i) {
return [];
}
@@ -572,12 +576,12 @@
''');
var previewInfo = run({
findNode.ifElement('=='): NodeChangeForIfElement()..conditionValue = false
- });
+ })!;
// This is a little kludgy; we could drop the `for` loop, but it's difficult
// to do so, and this is a rare enough corner case that it doesn't seem
// worth it. Replacing the `if` with `...{}` has the right effect, since
// it expands to nothing.
- expect(previewInfo.applyTo(code), '''
+ expect(previewInfo.applyTo(code!), '''
List<int> f(int i) {
return [for (var x in [1, 2, 3]) ...{}];
}
@@ -592,8 +596,8 @@
''');
var previewInfo = run({
findNode.ifElement('=='): NodeChangeForIfElement()..conditionValue = false
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
List<int> f(int i) {
return [i + 1];
}
@@ -608,8 +612,8 @@
''');
var previewInfo = run({
findNode.ifElement('=='): NodeChangeForIfElement()..conditionValue = true
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
List<int> f(int i) {
return [null];
}
@@ -625,8 +629,8 @@
var previewInfo = run({
findNode.conditionalExpression('=='): NodeChangeForConditionalExpression()
..conditionValue = false
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
int f(int i) {
return i + 1;
}
@@ -642,8 +646,8 @@
var previewInfo = run({
findNode.conditionalExpression('=='): NodeChangeForConditionalExpression()
..conditionValue = true
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
int f(int i) {
return null;
}
@@ -663,8 +667,8 @@
var previewInfo = run({
findNode.statement('if'): NodeChangeForIfStatement()
..conditionValue = false
- }, removeViaComments: true);
- expect(previewInfo.applyTo(code), '''
+ }, removeViaComments: true)!;
+ expect(previewInfo.applyTo(code!), '''
int f(int i) {
/* if (i == null) {
return null;
@@ -688,8 +692,8 @@
var previewInfo = run({
findNode.statement('if'): NodeChangeForIfStatement()
..conditionValue = true
- }, removeViaComments: true);
- expect(previewInfo.applyTo(code), '''
+ }, removeViaComments: true)!;
+ expect(previewInfo.applyTo(code!), '''
int f(int i) {
/* if (i == null) {
*/ return null; /*
@@ -712,8 +716,8 @@
var previewInfo = run({
findNode.statement('if'): NodeChangeForIfStatement()
..conditionValue = false
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
void f(int i) {}
''');
}
@@ -731,10 +735,10 @@
var previewInfo = run({
findNode.statement('if'): NodeChangeForIfStatement()
..conditionValue = false
- });
+ })!;
// Note: formatting is a little weird here but it's such a rare case that
// we don't care.
- expect(previewInfo.applyTo(code), '''
+ expect(previewInfo.applyTo(code!), '''
void f(int i) {
while (true)
{}
@@ -754,8 +758,8 @@
var previewInfo = run({
findNode.statement('if'): NodeChangeForIfStatement()
..conditionValue = true
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
void f(int i) {}
''');
}
@@ -773,8 +777,8 @@
var previewInfo = run({
findNode.statement('if'): NodeChangeForIfStatement()
..conditionValue = false
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
int f(int i) {
return i + 1;
}
@@ -794,8 +798,8 @@
var previewInfo = run({
findNode.statement('if'): NodeChangeForIfStatement()
..conditionValue = true
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
int f(int i) {
return i + 1;
}
@@ -819,8 +823,8 @@
var previewInfo = run({
findNode.statement('if'): NodeChangeForIfStatement()
..conditionValue = true
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
void f(int i, String callback()) {
{
var i = callback();
@@ -841,9 +845,9 @@
..addExpressionChange(
IntroduceAsChange(nnbdTypeProvider.intType, isDowncast: false),
_MockInfo())
- });
+ })!;
expect(
- previewInfo.applyTo(code), 'f(a, c) => a..b = (throw (c..d) as int);');
+ previewInfo.applyTo(code!), 'f(a, c) => a..b = (throw (c..d) as int);');
}
Future<void> test_introduceAs_dynamic() async {
@@ -854,8 +858,8 @@
..addExpressionChange(
IntroduceAsChange(nnbdTypeProvider.dynamicType, isDowncast: false),
_MockInfo())
- });
- expect(previewInfo.applyTo(code), 'f(Object o) => o as dynamic;');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f(Object o) => o as dynamic;');
}
Future<void> test_introduceAs_favorPrefix() async {
@@ -871,8 +875,8 @@
IntroduceAsChange(nnbdTypeProvider.futureNullType,
isDowncast: false),
_MockInfo())
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
import 'dart:async' as a;
import 'dart:async';
f(Object o) => o as a.Future<Null>;
@@ -893,8 +897,8 @@
nullabilitySuffix: NullabilitySuffix.none),
isDowncast: false),
_MockInfo())
- });
- expect(previewInfo.applyTo(code), 'f(Object o) => o as bool Function();');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f(Object o) => o as bool Function();');
}
Future<void> test_introduceAs_functionType_formal_bound() async {
@@ -914,8 +918,8 @@
nullabilitySuffix: NullabilitySuffix.none),
isDowncast: false),
_MockInfo())
- });
- expect(previewInfo.applyTo(code),
+ })!;
+ expect(previewInfo.applyTo(code!),
'f(Object o) => o as bool Function<T extends num>();');
}
@@ -936,9 +940,9 @@
nullabilitySuffix: NullabilitySuffix.none),
isDowncast: false),
_MockInfo())
- });
+ })!;
expect(
- previewInfo.applyTo(code), 'f(Object o) => o as bool Function<T>();');
+ previewInfo.applyTo(code!), 'f(Object o) => o as bool Function<T>();');
}
Future<void> test_introduceAs_functionType_formal_bound_object() async {
@@ -958,8 +962,8 @@
nullabilitySuffix: NullabilitySuffix.none),
isDowncast: false),
_MockInfo())
- });
- expect(previewInfo.applyTo(code),
+ })!;
+ expect(previewInfo.applyTo(code!),
'f(Object o) => o as bool Function<T extends Object>();');
}
@@ -982,9 +986,9 @@
nullabilitySuffix: NullabilitySuffix.none),
isDowncast: false),
_MockInfo())
- });
+ })!;
expect(
- previewInfo.applyTo(code), 'f(Object o) => o as bool Function<T>();');
+ previewInfo.applyTo(code!), 'f(Object o) => o as bool Function<T>();');
}
Future<void> test_introduceAs_functionType_formal_bound_question() async {
@@ -1005,8 +1009,8 @@
nullabilitySuffix: NullabilitySuffix.none),
isDowncast: false),
_MockInfo())
- });
- expect(previewInfo.applyTo(code),
+ })!;
+ expect(previewInfo.applyTo(code!),
'f(Object o) => o as bool Function<T extends num?>();');
}
@@ -1027,8 +1031,8 @@
nullabilitySuffix: NullabilitySuffix.none),
isDowncast: false),
_MockInfo())
- });
- expect(previewInfo.applyTo(code),
+ })!;
+ expect(previewInfo.applyTo(code!),
'f(Object o) => o as bool Function<T, U>();');
}
@@ -1051,8 +1055,8 @@
nullabilitySuffix: NullabilitySuffix.none),
isDowncast: false),
_MockInfo())
- });
- expect(previewInfo.applyTo(code),
+ })!;
+ expect(previewInfo.applyTo(code!),
'f(Object o) => o as bool Function(int, num);');
}
@@ -1075,8 +1079,8 @@
nullabilitySuffix: NullabilitySuffix.none),
isDowncast: false),
_MockInfo())
- });
- expect(previewInfo.applyTo(code),
+ })!;
+ expect(previewInfo.applyTo(code!),
'f(Object o) => o as bool Function({int x, num y});');
}
@@ -1099,8 +1103,8 @@
nullabilitySuffix: NullabilitySuffix.none),
isDowncast: false),
_MockInfo())
- });
- expect(previewInfo.applyTo(code),
+ })!;
+ expect(previewInfo.applyTo(code!),
'f(Object o) => o as bool Function([int, num]);');
}
@@ -1115,8 +1119,8 @@
nnbdTypeProvider.intType, nnbdTypeProvider.boolType),
isDowncast: false),
_MockInfo())
- });
- expect(previewInfo.applyTo(code), 'f(Object o) => o as Map<int, bool>;');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f(Object o) => o as Map<int, bool>;');
}
Future<void> test_introduceAs_no_parens() async {
@@ -1127,8 +1131,8 @@
..addExpressionChange(
IntroduceAsChange(nnbdTypeProvider.intType, isDowncast: false),
_MockInfo())
- });
- expect(previewInfo.applyTo(code), 'f(a, b) => a | b as int;');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f(a, b) => a | b as int;');
}
Future<void> test_introduceAs_parens() async {
@@ -1139,8 +1143,8 @@
..addExpressionChange(
IntroduceAsChange(nnbdTypeProvider.boolType, isDowncast: false),
_MockInfo())
- });
- expect(previewInfo.applyTo(code), 'f(a, b) => (a < b) as bool;');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f(a, b) => (a < b) as bool;');
}
Future<void> test_introduceAs_usePrefix() async {
@@ -1155,8 +1159,8 @@
IntroduceAsChange(nnbdTypeProvider.futureNullType,
isDowncast: false),
_MockInfo())
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
import 'dart:async' as a;
f(Object o) => o as a.Future<Null>;
''');
@@ -1171,8 +1175,8 @@
..addExpressionChange(
IntroduceAsChange(nnbdTypeProvider.intType, isDowncast: false),
_MockInfo())
- });
- expect(previewInfo.applyTo(code), 'f(x) => x! as int;');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f(x) => x! as int;');
}
Future<void> test_keep_redundant_parens() async {
@@ -1190,8 +1194,8 @@
MockDecoratedType(
MockDartType(toStringValueWithoutNullability: 'int')),
true)
- });
- expect(previewInfo.applyTo(code), 'f(int? x) {}');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f(int? x) {}');
}
Future<void> test_methodName_change() async {
@@ -1199,8 +1203,8 @@
var previewInfo = run({
findNode.methodInvocation('f();').methodName: NodeChangeForMethodName()
..replaceWith('g', null)
- });
- expect(previewInfo.applyTo(code), 'f() => g();');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f() => g();');
}
Future<void> test_methodName_no_change() async {
@@ -1220,10 +1224,10 @@
MockDecoratedType(
MockDartType(toStringValueWithoutNullability: 'int')),
false)
- });
- expect(previewInfo.applyTo(code), 'int x = 0;');
- expect(previewInfo.applyTo(code, includeInformative: true), 'int x = 0;');
- expect(previewInfo.values.single.single.info.description.appliedMessage,
+ })!;
+ expect(previewInfo.applyTo(code!), 'int x = 0;');
+ expect(previewInfo.applyTo(code!, includeInformative: true), 'int x = 0;');
+ expect(previewInfo.values.single.single.info!.description.appliedMessage,
"Type 'int' was not made nullable");
}
@@ -1241,9 +1245,9 @@
literal: NodeChangeForExpression()
..addExpressionChange(
NoValidMigrationChange(MockDartType()), _MockInfo())
- });
- expect(previewInfo.applyTo(code), code);
- expect(previewInfo.applyTo(code, includeInformative: true),
+ })!;
+ expect(previewInfo.applyTo(code!), code);
+ expect(previewInfo.applyTo(code!, includeInformative: true),
'f(a) => null /* no valid migration */;');
}
@@ -1253,8 +1257,8 @@
var previewInfo = run({
index: NodeChangeForExpression()
..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo())
- });
- expect(previewInfo.applyTo(code), 'f(a) => a..[0]!.c;');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f(a) => a..[0]!.c;');
}
Future<void> test_nullCheck_methodInvocation_cascadeResult() async {
@@ -1263,8 +1267,8 @@
var previewInfo = run({
method: NodeChangeForMethodInvocation()
..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo())
- });
- expect(previewInfo.applyTo(code), 'f(a) => a..b()!.c;');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f(a) => a..b()!.c;');
}
Future<void> test_nullCheck_no_parens() async {
@@ -1273,8 +1277,8 @@
var previewInfo = run({
expr: NodeChangeForExpression()
..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo())
- });
- expect(previewInfo.applyTo(code), 'f(a) => a++!;');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f(a) => a++!;');
}
Future<void> test_nullCheck_parens() async {
@@ -1283,8 +1287,8 @@
var previewInfo = run({
expr: NodeChangeForExpression()
..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo())
- });
- expect(previewInfo.applyTo(code), 'f(a) => (-a)!;');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f(a) => (-a)!;');
}
Future<void> test_nullCheck_propertyAccess_cascadeResult() async {
@@ -1293,8 +1297,8 @@
var previewInfo = run({
property: NodeChangeForPropertyAccess()
..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo())
- });
- expect(previewInfo.applyTo(code), 'f(a) => a..b!.c;');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f(a) => a..b!.c;');
}
Future<void> test_parameter_addExplicitType_annotated() async {
@@ -1302,8 +1306,8 @@
var previewInfo = run({
findNode.simpleParameter('x'): NodeChangeForSimpleFormalParameter()
..addExplicitType = nnbdTypeProvider.intType
- });
- expect(previewInfo.applyTo(code), 'f({@deprecated int x = 0}) {}');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f({@deprecated int x = 0}) {}');
}
Future<void> test_parameter_addExplicitType_declared_with_covariant() async {
@@ -1318,8 +1322,8 @@
var previewInfo = run({
findNode.simpleParameter('x = 3'): NodeChangeForSimpleFormalParameter()
..addExplicitType = nnbdTypeProvider.intType
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
class C {
m({num x}) {}
}
@@ -1334,8 +1338,8 @@
var previewInfo = run({
findNode.simpleParameter('x'): NodeChangeForSimpleFormalParameter()
..addExplicitType = nnbdTypeProvider.intType
- });
- expect(previewInfo.applyTo(code), 'f({final int x = 0}) {}');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f({final int x = 0}) {}');
}
Future<void> test_parameter_addExplicitType_declared_with_var() async {
@@ -1343,8 +1347,8 @@
var previewInfo = run({
findNode.simpleParameter('x'): NodeChangeForSimpleFormalParameter()
..addExplicitType = nnbdTypeProvider.intType
- });
- expect(previewInfo.applyTo(code), 'f({int x = 0}) {}');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f({int x = 0}) {}');
}
Future<void> test_parameter_addExplicitType_named() async {
@@ -1352,8 +1356,8 @@
var previewInfo = run({
findNode.simpleParameter('x'): NodeChangeForSimpleFormalParameter()
..addExplicitType = nnbdTypeProvider.intType
- });
- expect(previewInfo.applyTo(code), 'f({int x = 0}) {}');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f({int x = 0}) {}');
}
Future<void> test_parameter_addExplicitType_no() async {
@@ -1368,8 +1372,8 @@
var previewInfo = run({
findNode.simpleParameter('x'): NodeChangeForSimpleFormalParameter()
..addExplicitType = nnbdTypeProvider.intType
- });
- expect(previewInfo.applyTo(code), 'f([int x = 0]) {}');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f([int x = 0]) {}');
}
Future<void> test_parameter_addExplicitType_prefixed_type() async {
@@ -1380,8 +1384,8 @@
var previewInfo = run({
findNode.simpleParameter('x'): NodeChangeForSimpleFormalParameter()
..addExplicitType = nnbdTypeProvider.intType
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
import 'dart:core' as core;
f({core.int x = 0}) {}
''');
@@ -1398,8 +1402,8 @@
findNode.fieldFormalParameter('this.x'):
NodeChangeForFieldFormalParameter()
..addExplicitType = nnbdTypeProvider.intType
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
class C {
int x;
C(int this.x) {}
@@ -1419,8 +1423,8 @@
findNode.fieldFormalParameter('this.x'):
NodeChangeForFieldFormalParameter()
..addExplicitType = nnbdTypeProvider.intType
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
class C {
int x;
C(final int this.x) {}
@@ -1440,8 +1444,8 @@
findNode.fieldFormalParameter('this.x'):
NodeChangeForFieldFormalParameter()
..addExplicitType = nnbdTypeProvider.intType
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
class C {
int x;
C(int this.x) {}
@@ -1469,8 +1473,8 @@
var previewInfo = run({
findNode.postfix('++'): NodeChangeForPostfixExpression()
..addExpressionChange(NullCheckChange(MockDartType()), null)
- });
- expect(previewInfo.applyTo(code), 'f(int x) => x++!;');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f(int x) => x++!;');
}
Future<void> test_post_increment_change_target() async {
@@ -1480,8 +1484,8 @@
findNode.postfix('++'): NodeChangeForPostfixExpression(),
findNode.index('[0]').target: NodeChangeForExpression()
..addExpressionChange(NullCheckChange(MockDartType()), null)
- });
- expect(previewInfo.applyTo(code), 'f(List<int> x) => x![0]++;');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f(List<int> x) => x![0]++;');
}
Future<void> test_post_increment_introduce_as() async {
@@ -1492,8 +1496,8 @@
..addExpressionChange(
IntroduceAsChange(nnbdTypeProvider.intType, isDowncast: false),
null)
- });
- expect(previewInfo.applyTo(code), 'f(int x) => x++ as int;');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f(int x) => x++ as int;');
}
Future<void> test_post_increment_with_bad_combined_type() async {
@@ -1502,11 +1506,11 @@
var previewInfo = run({
findNode.postfix('++'): NodeChangeForPostfixExpression()
..hasBadCombinedType = true
- });
- expect(previewInfo.applyTo(code), content);
+ })!;
+ expect(previewInfo.applyTo(code!), content);
expect(previewInfo, hasLength(1));
- var edit = previewInfo[content.indexOf('++')].single;
- expect(edit.info.description,
+ var edit = previewInfo[content.indexOf('++')]!.single;
+ expect(edit.info!.description,
NullabilityFixDescription.compoundAssignmentHasBadCombinedType);
expect(edit.isInformative, isTrue);
expect(edit.length, '++'.length);
@@ -1518,11 +1522,11 @@
var previewInfo = run({
findNode.postfix('++'): NodeChangeForPostfixExpression()
..hasNullableSource = true
- });
- expect(previewInfo.applyTo(code), content);
+ })!;
+ expect(previewInfo.applyTo(code!), content);
expect(previewInfo, hasLength(1));
- var edit = previewInfo[content.indexOf('++')].single;
- expect(edit.info.description,
+ var edit = previewInfo[content.indexOf('++')]!.single;
+ expect(edit.info!.description,
NullabilityFixDescription.compoundAssignmentHasNullableSource);
expect(edit.isInformative, isTrue);
expect(edit.length, '++'.length);
@@ -1534,8 +1538,8 @@
var previewInfo = run({
findNode.prefix('++'): NodeChangeForPrefixExpression()
..addExpressionChange(NullCheckChange(MockDartType()), null)
- });
- expect(previewInfo.applyTo(code), 'f(int x) => (++x)!;');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f(int x) => (++x)!;');
}
Future<void> test_pre_increment_change_target() async {
@@ -1545,8 +1549,8 @@
findNode.prefix('++'): NodeChangeForPrefixExpression(),
findNode.index('[0]').target: NodeChangeForExpression()
..addExpressionChange(NullCheckChange(MockDartType()), null)
- });
- expect(previewInfo.applyTo(code), 'f(List<int> x) => ++x![0];');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f(List<int> x) => ++x![0];');
}
Future<void> test_pre_increment_introduce_as() async {
@@ -1557,8 +1561,8 @@
..addExpressionChange(
IntroduceAsChange(nnbdTypeProvider.intType, isDowncast: false),
null)
- });
- expect(previewInfo.applyTo(code), 'f(int x) => ++x as int;');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f(int x) => ++x as int;');
}
Future<void> test_pre_increment_with_bad_combined_type() async {
@@ -1567,11 +1571,11 @@
var previewInfo = run({
findNode.prefix('++'): NodeChangeForPrefixExpression()
..hasBadCombinedType = true
- });
- expect(previewInfo.applyTo(code), content);
+ })!;
+ expect(previewInfo.applyTo(code!), content);
expect(previewInfo, hasLength(1));
- var edit = previewInfo[content.indexOf('++')].single;
- expect(edit.info.description,
+ var edit = previewInfo[content.indexOf('++')]!.single;
+ expect(edit.info!.description,
NullabilityFixDescription.compoundAssignmentHasBadCombinedType);
expect(edit.isInformative, isTrue);
expect(edit.length, '++'.length);
@@ -1583,11 +1587,11 @@
var previewInfo = run({
findNode.prefix('++'): NodeChangeForPrefixExpression()
..hasNullableSource = true
- });
- expect(previewInfo.applyTo(code), content);
+ })!;
+ expect(previewInfo.applyTo(code!), content);
expect(previewInfo, hasLength(1));
- var edit = previewInfo[content.indexOf('++')].single;
- expect(edit.info.description,
+ var edit = previewInfo[content.indexOf('++')]!.single;
+ expect(edit.info!.description,
NullabilityFixDescription.compoundAssignmentHasNullableSource);
expect(edit.isInformative, isTrue);
expect(edit.length, '++'.length);
@@ -1597,9 +1601,10 @@
test_removeAs_in_cascade_target_no_parens_needed_cascade() async {
await analyze('f(a) => ((a..b) as dynamic)..c;');
var cascade = findNode.cascade('a..b');
- var cast = cascade.parent.parent;
- var previewInfo = run({cast: NodeChangeForAsExpression()..removeAs = true});
- expect(previewInfo.applyTo(code), 'f(a) => a..b..c;');
+ var cast = cascade.parent!.parent;
+ var previewInfo =
+ run({cast: NodeChangeForAsExpression()..removeAs = true})!;
+ expect(previewInfo.applyTo(code!), 'f(a) => a..b..c;');
}
Future<void>
@@ -1608,43 +1613,46 @@
// clarity, even though they're not needed?
await analyze('f(a, b, c) => ((a ? b : c) as dynamic)..d;');
var conditional = findNode.conditionalExpression('a ? b : c');
- var cast = conditional.parent.parent;
- var previewInfo = run({cast: NodeChangeForAsExpression()..removeAs = true});
- expect(previewInfo.applyTo(code), 'f(a, b, c) => a ? b : c..d;');
+ var cast = conditional.parent!.parent;
+ var previewInfo =
+ run({cast: NodeChangeForAsExpression()..removeAs = true})!;
+ expect(previewInfo.applyTo(code!), 'f(a, b, c) => a ? b : c..d;');
}
Future<void>
test_removeAs_in_cascade_target_parens_needed_assignment() async {
await analyze('f(a, b) => ((a = b) as dynamic)..c;');
var assignment = findNode.assignment('a = b');
- var cast = assignment.parent.parent;
- var previewInfo = run({cast: NodeChangeForAsExpression()..removeAs = true});
- expect(previewInfo.applyTo(code), 'f(a, b) => (a = b)..c;');
+ var cast = assignment.parent!.parent;
+ var previewInfo =
+ run({cast: NodeChangeForAsExpression()..removeAs = true})!;
+ expect(previewInfo.applyTo(code!), 'f(a, b) => (a = b)..c;');
}
Future<void> test_removeAs_in_cascade_target_parens_needed_throw() async {
await analyze('f(a) => ((throw a) as dynamic)..b;');
var throw_ = findNode.throw_('throw a');
- var cast = throw_.parent.parent;
- var previewInfo = run({cast: NodeChangeForAsExpression()..removeAs = true});
- expect(previewInfo.applyTo(code), 'f(a) => (throw a)..b;');
+ var cast = throw_.parent!.parent;
+ var previewInfo =
+ run({cast: NodeChangeForAsExpression()..removeAs = true})!;
+ expect(previewInfo.applyTo(code!), 'f(a) => (throw a)..b;');
}
Future<void>
test_removeAs_lower_precedence_do_not_remove_inner_parens() async {
await analyze('f(a, b, c) => (a == b) as Null == c;');
var expr = findNode.binary('a == b');
- var previewInfo =
- run({expr.parent.parent: NodeChangeForAsExpression()..removeAs = true});
- expect(previewInfo.applyTo(code), 'f(a, b, c) => (a == b) == c;');
+ var previewInfo = run(
+ {expr.parent!.parent: NodeChangeForAsExpression()..removeAs = true})!;
+ expect(previewInfo.applyTo(code!), 'f(a, b, c) => (a == b) == c;');
}
Future<void> test_removeAs_lower_precedence_remove_inner_parens() async {
await analyze('f(a, b) => (a == b) as Null;');
var expr = findNode.binary('a == b');
- var previewInfo =
- run({expr.parent.parent: NodeChangeForAsExpression()..removeAs = true});
- expect(previewInfo.applyTo(code), 'f(a, b) => a == b;');
+ var previewInfo = run(
+ {expr.parent!.parent: NodeChangeForAsExpression()..removeAs = true})!;
+ expect(previewInfo.applyTo(code!), 'f(a, b) => a == b;');
}
Future<void> test_removeAs_parens_needed_due_to_cascade() async {
@@ -1660,51 +1668,54 @@
// corner case so for now we're not worrying about it.
await analyze('f(a, c) => a..b = throw (c..d) as int;');
var cd = findNode.cascade('c..d');
- var cast = cd.parent.parent;
- var previewInfo = run({cast: NodeChangeForAsExpression()..removeAs = true});
- expect(previewInfo.applyTo(code), 'f(a, c) => a..b = (throw c..d);');
+ var cast = cd.parent!.parent;
+ var previewInfo =
+ run({cast: NodeChangeForAsExpression()..removeAs = true})!;
+ expect(previewInfo.applyTo(code!), 'f(a, c) => a..b = (throw c..d);');
}
Future<void>
test_removeAs_parens_needed_due_to_cascade_in_conditional_else() async {
await analyze('f(a, b, c) => a ? b : (c..d) as int;');
var cd = findNode.cascade('c..d');
- var cast = cd.parent.parent;
- var previewInfo = run({cast: NodeChangeForAsExpression()..removeAs = true});
- expect(previewInfo.applyTo(code), 'f(a, b, c) => a ? b : (c..d);');
+ var cast = cd.parent!.parent;
+ var previewInfo =
+ run({cast: NodeChangeForAsExpression()..removeAs = true})!;
+ expect(previewInfo.applyTo(code!), 'f(a, b, c) => a ? b : (c..d);');
}
Future<void>
test_removeAs_parens_needed_due_to_cascade_in_conditional_then() async {
await analyze('f(a, b, d) => a ? (b..c) as int : d;');
var bc = findNode.cascade('b..c');
- var cast = bc.parent.parent;
- var previewInfo = run({cast: NodeChangeForAsExpression()..removeAs = true});
- expect(previewInfo.applyTo(code), 'f(a, b, d) => a ? (b..c) : d;');
+ var cast = bc.parent!.parent;
+ var previewInfo =
+ run({cast: NodeChangeForAsExpression()..removeAs = true})!;
+ expect(previewInfo.applyTo(code!), 'f(a, b, d) => a ? (b..c) : d;');
}
Future<void> test_removeAs_raise_precedence_do_not_remove_parens() async {
await analyze('f(a, b, c) => a | (b | c as int);');
var expr = findNode.binary('b | c');
var previewInfo =
- run({expr.parent: NodeChangeForAsExpression()..removeAs = true});
- expect(previewInfo.applyTo(code), 'f(a, b, c) => a | (b | c);');
+ run({expr.parent: NodeChangeForAsExpression()..removeAs = true})!;
+ expect(previewInfo.applyTo(code!), 'f(a, b, c) => a | (b | c);');
}
Future<void> test_removeAs_raise_precedence_no_parens_to_remove() async {
await analyze('f(a, b, c) => a = b | c as int;');
var expr = findNode.binary('b | c');
var previewInfo =
- run({expr.parent: NodeChangeForAsExpression()..removeAs = true});
- expect(previewInfo.applyTo(code), 'f(a, b, c) => a = b | c;');
+ run({expr.parent: NodeChangeForAsExpression()..removeAs = true})!;
+ expect(previewInfo.applyTo(code!), 'f(a, b, c) => a = b | c;');
}
Future<void> test_removeAs_raise_precedence_remove_parens() async {
await analyze('f(a, b, c) => a < (b | c as int);');
var expr = findNode.binary('b | c');
var previewInfo =
- run({expr.parent: NodeChangeForAsExpression()..removeAs = true});
- expect(previewInfo.applyTo(code), 'f(a, b, c) => a < b | c;');
+ run({expr.parent: NodeChangeForAsExpression()..removeAs = true})!;
+ expect(previewInfo.applyTo(code!), 'f(a, b, c) => a < b | c;');
}
Future<void> test_removeLanguageVersion() async {
@@ -1715,9 +1726,9 @@
var previewInfo = run({
findNode.unit: NodeChangeForCompilationUnit()
..removeLanguageVersionComment = true
- });
+ })!;
// TODO(mfairhurst): Remove beginning \n once it renders properly in preview
- expect(previewInfo.applyTo(code), '\nvoid main() {}\n');
+ expect(previewInfo.applyTo(code!), '\nvoid main() {}\n');
}
Future<void> test_removeLanguageVersion_after_license() async {
@@ -1731,9 +1742,9 @@
var previewInfo = run({
findNode.unit: NodeChangeForCompilationUnit()
..removeLanguageVersionComment = true
- });
+ })!;
// TODO(mfairhurst): Remove beginning \n once it renders properly in preview
- expect(previewInfo.applyTo(code), '''
+ expect(previewInfo.applyTo(code!), '''
// Some licensing stuff here...
// Some copyrighting stuff too...
// etc...
@@ -1750,9 +1761,9 @@
var previewInfo = run({
findNode.unit: NodeChangeForCompilationUnit()
..removeLanguageVersionComment = true
- });
+ })!;
// TODO(mfairhurst): Remove beginning \n once it renders properly in preview
- expect(previewInfo.applyTo(code), '\nvoid main() {}\n');
+ expect(previewInfo.applyTo(code!), '\nvoid main() {}\n');
}
Future<void> test_removeLanguageVersion_withOtherChanges() async {
@@ -1768,9 +1779,9 @@
MockDecoratedType(
MockDartType(toStringValueWithoutNullability: 'int')),
true)
- });
+ })!;
// TODO(mfairhurst): Remove beginning \n once it renders properly in preview
- expect(previewInfo.applyTo(code), '\nint? f() => null;\n');
+ expect(previewInfo.applyTo(code!), '\nint? f() => null;\n');
}
Future<void> test_removeNullAwarenessFromMethodInvocation() async {
@@ -1779,8 +1790,8 @@
var previewInfo = run({
methodInvocation: NodeChangeForMethodInvocation()
..removeNullAwareness = true
- });
- expect(previewInfo.applyTo(code), 'f(x) => x.m();');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f(x) => x.m();');
}
Future<void>
@@ -1793,8 +1804,8 @@
..removeNullAwareness = true,
argument: NodeChangeForExpression()
..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo())
- });
- expect(previewInfo.applyTo(code), 'f(x) => x.m(x!);');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f(x) => x.m(x!);');
}
Future<void>
@@ -1806,8 +1817,8 @@
methodInvocation: NodeChangeForMethodInvocation()
..removeNullAwareness = true,
cast: NodeChangeForAsExpression()..removeAs = true
- });
- expect(previewInfo.applyTo(code), 'f(x) => x.m();');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f(x) => x.m();');
}
Future<void>
@@ -1823,8 +1834,8 @@
MockDecoratedType(
MockDartType(toStringValueWithoutNullability: 'int')),
true)
- });
- expect(previewInfo.applyTo(code), 'f(x) => x.m<int?>();');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f(x) => x.m<int?>();');
}
Future<void> test_removeNullAwarenessFromPropertyAccess() async {
@@ -1832,8 +1843,8 @@
var propertyAccess = findNode.propertyAccess('?.');
var previewInfo = run({
propertyAccess: NodeChangeForPropertyAccess()..removeNullAwareness = true
- });
- expect(previewInfo.applyTo(code), 'f(x) => x.y;');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f(x) => x.y;');
}
Future<void> test_removeNullAwarenessFromPropertyAccess_changeTarget() async {
@@ -1843,8 +1854,8 @@
var previewInfo = run({
propertyAccess: NodeChangeForPropertyAccess()..removeNullAwareness = true,
cast: NodeChangeForAsExpression()..removeAs = true
- });
- expect(previewInfo.applyTo(code), 'f(x) => x.y;');
+ })!;
+ expect(previewInfo.applyTo(code!), 'f(x) => x.y;');
}
Future<void>
@@ -1857,8 +1868,8 @@
var annotation = findNode.annotation('required');
var previewInfo = run({
annotation: NodeChangeForAnnotation()..changeToRequiredKeyword = true
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
import 'package:meta/meta.dart';
f({@deprecated required int x}) {}
''');
@@ -1874,8 +1885,8 @@
var annotation = findNode.annotation('required');
var previewInfo = run({
annotation: NodeChangeForAnnotation()..changeToRequiredKeyword = true
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
import 'package:meta/meta.dart' as meta;
f({required int x}) {}
''');
@@ -1892,8 +1903,8 @@
var annotation = findNode.annotation('@foo');
var previewInfo = run({
annotation: NodeChangeForAnnotation()..changeToRequiredKeyword = true
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
import 'package:meta/meta.dart';
const foo = required;
f({required int x}) {}
@@ -1909,8 +1920,8 @@
var annotation = findNode.annotation('required');
var previewInfo = run({
annotation: NodeChangeForAnnotation()..changeToRequiredKeyword = true
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
import 'package:meta/meta.dart';
f({required int x}) {}
''');
@@ -1927,8 +1938,8 @@
var annotation = findNode.annotation('required');
var previewInfo = run(
{annotation: NodeChangeForAnnotation()..changeToRequiredKeyword = true},
- removeViaComments: true);
- expect(previewInfo.applyTo(code), '''
+ removeViaComments: true)!;
+ expect(previewInfo.applyTo(code!), '''
import 'package:meta/meta.dart';
f({required int x}) {}
''');
@@ -1941,8 +1952,8 @@
findNode.variableDeclarationList('final'):
NodeChangeForVariableDeclarationList()
..addExplicitType = nnbdTypeProvider.intType
- });
- expect(previewInfo.applyTo(code), 'final int x = 0;');
+ })!;
+ expect(previewInfo.applyTo(code!), 'final int x = 0;');
}
Future<void> test_variableDeclarationList_addExplicitType_metadata() async {
@@ -1951,8 +1962,8 @@
findNode.variableDeclarationList('var'):
NodeChangeForVariableDeclarationList()
..addExplicitType = nnbdTypeProvider.intType
- });
- expect(previewInfo.applyTo(code), '@deprecated int x = 0;');
+ })!;
+ expect(previewInfo.applyTo(code!), '@deprecated int x = 0;');
}
Future<void> test_variableDeclarationList_addExplicitType_no() async {
@@ -1972,8 +1983,8 @@
..addExplicitType = nnbdTypeProvider.intType,
findNode.integerLiteral('0'): NodeChangeForExpression()
..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo())
- });
- expect(previewInfo.applyTo(code), 'int x = 0!;');
+ })!;
+ expect(previewInfo.applyTo(code!), 'int x = 0!;');
}
Future<void> test_variableDeclarationList_addExplicitType_prefixed() async {
@@ -1985,8 +1996,8 @@
findNode.variableDeclarationList('final'):
NodeChangeForVariableDeclarationList()
..addExplicitType = nnbdTypeProvider.intType
- });
- expect(previewInfo.applyTo(code), '''
+ })!;
+ expect(previewInfo.applyTo(code!), '''
import 'dart:core' as core;
final core.int x = 0;
''');
@@ -1998,8 +2009,8 @@
findNode.variableDeclarationList('var'):
NodeChangeForVariableDeclarationList()
..addExplicitType = nnbdTypeProvider.intType
- });
- expect(previewInfo.applyTo(code), 'int x = 0;');
+ })!;
+ expect(previewInfo.applyTo(code!), 'int x = 0;');
}
Future<void> test_warnOnDeadIf_false() async {
@@ -2011,8 +2022,8 @@
var previewInfo = run({
findNode.statement('if'): NodeChangeForIfStatement()
..conditionValue = false
- }, warnOnWeakCode: true);
- expect(previewInfo.applyTo(code, includeInformative: true), '''
+ }, warnOnWeakCode: true)!;
+ expect(previewInfo.applyTo(code!, includeInformative: true), '''
f(int i) {
if (i == null /* == false */) print(i);
}
@@ -2028,8 +2039,8 @@
var previewInfo = run({
findNode.statement('if'): NodeChangeForIfStatement()
..conditionValue = true
- }, warnOnWeakCode: true);
- expect(previewInfo.applyTo(code, includeInformative: true), '''
+ }, warnOnWeakCode: true)!;
+ expect(previewInfo.applyTo(code!, includeInformative: true), '''
f(int i) {
if (i != null /* == true */) print(i);
}
@@ -2046,34 +2057,34 @@
var previewInfo = run({
findNode.propertyAccess('?.'): NodeChangeForPropertyAccess()
..removeNullAwareness = true
- }, warnOnWeakCode: true);
- expect(previewInfo.applyTo(code), content);
+ }, warnOnWeakCode: true)!;
+ expect(previewInfo.applyTo(code!), content);
expect(previewInfo, hasLength(1));
- var edit = previewInfo[content.indexOf('?')].single;
+ var edit = previewInfo[content.indexOf('?')]!.single;
expect(edit.isInformative, isTrue);
expect(edit.length, '?'.length);
}
}
class FixAggregatorTestBase extends AbstractSingleUnitTest {
- String code;
+ String? code;
Future<void> analyze(String code) async {
this.code = code;
await resolveTestUnit(code);
}
- Map<int, List<AtomicEdit>> run(Map<AstNode, NodeChange> changes,
+ Map<int?, List<AtomicEdit>>? run(Map<AstNode?, NodeChange> changes,
{bool removeViaComments = false, bool warnOnWeakCode = false}) {
- return FixAggregator.run(testUnit, testCode, changes,
+ return FixAggregator.run(testUnit!, testCode, changes,
removeViaComments: removeViaComments, warnOnWeakCode: warnOnWeakCode);
}
}
class MockDartType implements TypeImpl {
- final String toStringValueWithNullability;
+ final String? toStringValueWithNullability;
- final String toStringValueWithoutNullability;
+ final String? toStringValueWithoutNullability;
const MockDartType(
{this.toStringValueWithNullability,
@@ -2085,8 +2096,8 @@
bool withNullability = false,
}) {
var result = withNullability
- ? toStringValueWithNullability
- : toStringValueWithoutNullability;
+ ? toStringValueWithNullability!
+ : toStringValueWithoutNullability!;
expect(result, isNotNull);
return result;
}
diff --git a/pkg/nnbd_migration/test/fix_builder_test.dart b/pkg/nnbd_migration/test/fix_builder_test.dart
index c3a53aa..a9026b2 100644
--- a/pkg/nnbd_migration/test/fix_builder_test.dart
+++ b/pkg/nnbd_migration/test/fix_builder_test.dart
@@ -31,10 +31,10 @@
/// The type that the assignment target has when read. This is only relevant
/// for compound assignments (since they both read and write the assignment
/// target)
- final DartType readType;
+ final DartType? readType;
/// The type that the assignment target has when written to.
- final DartType writeType;
+ final DartType? writeType;
AssignmentTargetInfo(this.readType, this.writeType);
}
@@ -135,7 +135,7 @@
.having((c) => c.replacement, 'replacement', replacement);
Map<AstNode, NodeChange> scopedChanges(
- FixBuilder fixBuilder, AstNode scope) =>
+ FixBuilder fixBuilder, AstNode? scope) =>
{
for (var entry in fixBuilder.changes.entries)
if (_isInScope(entry.key, scope) && !entry.value.isInformative)
@@ -151,7 +151,7 @@
};
Map<AstNode, Set<Problem>> scopedProblems(
- FixBuilder fixBuilder, AstNode scope) =>
+ FixBuilder fixBuilder, AstNode? scope) =>
{
for (var entry in fixBuilder.problems.entries)
if (_isInScope(entry.key, scope)) entry.key: entry.value
@@ -3799,7 +3799,7 @@
{Map<AstNode, Matcher> changes = const <Expression, Matcher>{},
Map<AstNode, Set<Problem>> problems = const <AstNode, Set<Problem>>{},
bool injectNeedsIterableExtension = false}) {
- var fixBuilder = _createFixBuilder(testUnit);
+ var fixBuilder = _createFixBuilder(testUnit!);
if (injectNeedsIterableExtension) {
fixBuilder.needsIterableExtension = true;
}
@@ -3809,7 +3809,7 @@
}
void visitAssignmentTarget(
- Expression node, String expectedReadType, String expectedWriteType,
+ Expression node, String? expectedReadType, String expectedWriteType,
{Map<AstNode, Matcher> changes = const <Expression, Matcher>{},
Map<AstNode, Set<Problem>> problems = const <AstNode, Set<Problem>>{}}) {
var fixBuilder = _createFixBuilder(node);
@@ -3818,10 +3818,10 @@
if (expectedReadType == null) {
expect(targetInfo.readType, null);
} else {
- expect(targetInfo.readType.getDisplayString(withNullability: true),
+ expect(targetInfo.readType!.getDisplayString(withNullability: true),
expectedReadType);
}
- expect(targetInfo.writeType.getDisplayString(withNullability: true),
+ expect(targetInfo.writeType!.getDisplayString(withNullability: true),
expectedWriteType);
expect(scopedChanges(fixBuilder, node), changes);
expect(scopedProblems(fixBuilder, node), problems);
@@ -3837,12 +3837,12 @@
}
FixBuilder visitSubexpression(Expression node, String expectedType,
- {Map<AstNode, Matcher> changes = const <Expression, Matcher>{},
+ {Map<AstNode?, Matcher> changes = const <Expression, Matcher>{},
Map<AstNode, Set<Problem>> problems = const <AstNode, Set<Problem>>{},
bool warnOnWeakCode = false}) {
var fixBuilder = _createFixBuilder(node, warnOnWeakCode: warnOnWeakCode);
fixBuilder.visitAll();
- var type = node.staticType;
+ var type = node.staticType!;
expect(type.getDisplayString(withNullability: true), expectedType);
expect(scopedChanges(fixBuilder, node), changes);
expect(scopedProblems(fixBuilder, node), problems);
@@ -3855,7 +3855,7 @@
dynamic informative = anything}) {
var fixBuilder = _createFixBuilder(node);
fixBuilder.visitAll();
- var type = node.type;
+ var type = node.type!;
expect(type.getDisplayString(withNullability: true), expectedType);
expect(scopedChanges(fixBuilder, node), changes);
expect(scopedProblems(fixBuilder, node), problems);
@@ -3868,7 +3868,7 @@
assert(
identical(ElementTypeProvider.current, const ElementTypeProvider()));
ElementTypeProvider.current = fixBuilder.migrationResolutionHooks;
- var assignment = node.thisOrAncestorOfType<AssignmentExpression>();
+ var assignment = node.thisOrAncestorOfType<AssignmentExpression>()!;
var readType = assignment.readType;
var writeType = assignment.writeType;
return AssignmentTargetInfo(readType, writeType);
@@ -3878,8 +3878,8 @@
}
FixBuilder _createFixBuilder(AstNode scope, {bool warnOnWeakCode = false}) {
- var unit = scope.thisOrAncestorOfType<CompilationUnit>();
- var definingLibrary = unit.declaredElement.library;
+ var unit = scope.thisOrAncestorOfType<CompilationUnit>()!;
+ var definingLibrary = unit.declaredElement!.library;
return FixBuilder(
testSource,
decoratedClassHierarchy,
@@ -3893,7 +3893,7 @@
graph, {});
}
- bool _isInScope(AstNode node, AstNode scope) {
+ bool _isInScope(AstNode node, AstNode? scope) {
return node
.thisOrAncestorMatching((ancestor) => identical(ancestor, scope)) !=
null;
diff --git a/pkg/nnbd_migration/test/front_end/analysis_abstract.dart b/pkg/nnbd_migration/test/front_end/analysis_abstract.dart
index 1302e28..dea20d0 100644
--- a/pkg/nnbd_migration/test/front_end/analysis_abstract.dart
+++ b/pkg/nnbd_migration/test/front_end/analysis_abstract.dart
@@ -8,27 +8,28 @@
/// An abstract base for all 'analysis' domain tests.
class AbstractAnalysisTest extends AbstractContextTest {
- String projectPath;
- String testFolder;
- String testFile;
- String testCode;
+ String? projectPath;
+ String? testFolder;
+ String? testFile;
+ late String testCode;
AbstractAnalysisTest();
void addAnalysisOptionsFile(String content) {
newFile(
- resourceProvider.pathContext.join(projectPath, 'analysis_options.yaml'),
+ resourceProvider.pathContext
+ .join(projectPath!, 'analysis_options.yaml'),
content: content);
}
- String addTestFile(String content) {
- newFile(testFile, content: content);
+ String? addTestFile(String content) {
+ newFile(testFile!, content: content);
testCode = content;
return testFile;
}
/// Create an analysis options file based on the given arguments.
- void createAnalysisOptionsFile({List<String> experiments}) {
+ void createAnalysisOptionsFile({List<String>? experiments}) {
var buffer = StringBuffer();
if (experiments != null) {
buffer.writeln('analyzer:');
@@ -41,8 +42,8 @@
}
/// Creates a project [projectPath].
- void createProject({Map<String, String> packageRoots}) {
- newFolder(projectPath);
+ void createProject({Map<String, String>? packageRoots}) {
+ newFolder(projectPath!);
}
/// Returns the offset of [search] in the file at the given [path].
@@ -63,8 +64,8 @@
return offset;
}
- String modifyTestFile(String content) {
- modifyFile(testFile, content);
+ String? modifyTestFile(String content) {
+ modifyFile(testFile!, content);
testCode = content;
return testFile;
}
diff --git a/pkg/nnbd_migration/test/front_end/info_builder_test.dart b/pkg/nnbd_migration/test/front_end/info_builder_test.dart
index 625672b..e4da1b7 100644
--- a/pkg/nnbd_migration/test/front_end/info_builder_test.dart
+++ b/pkg/nnbd_migration/test/front_end/info_builder_test.dart
@@ -4,7 +4,6 @@
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/ast/ast.dart';
-import 'package:meta/meta.dart';
import 'package:nnbd_migration/nnbd_migration.dart';
import 'package:nnbd_migration/src/front_end/info_builder.dart';
import 'package:nnbd_migration/src/front_end/migration_info.dart';
@@ -25,7 +24,7 @@
@reflectiveTest
class BuildEnclosingMemberDescriptionTest extends AbstractAnalysisTest {
Future<ResolvedUnitResult> resolveTestFile() async {
- return await session.getResolvedUnit2(testFile) as ResolvedUnitResult;
+ return await session.getResolvedUnit2(testFile!) as ResolvedUnitResult;
}
Future<void> test_classConstructor_named() async {
@@ -36,7 +35,7 @@
''');
var result = await resolveTestFile();
ClassDeclaration class_ =
- result.unit.declarations.single as ClassDeclaration;
+ result.unit!.declarations.single as ClassDeclaration;
var constructor = class_.members.single;
expect(InfoBuilder.buildEnclosingMemberDescription(constructor),
equals("the constructor 'C.aaa'"));
@@ -50,7 +49,7 @@
''');
var result = await resolveTestFile();
ClassDeclaration class_ =
- result.unit.declarations.single as ClassDeclaration;
+ result.unit!.declarations.single as ClassDeclaration;
var constructor = class_.members.single;
expect(InfoBuilder.buildEnclosingMemberDescription(constructor),
equals("the default constructor of 'C'"));
@@ -64,7 +63,7 @@
''');
var result = await resolveTestFile();
ClassDeclaration class_ =
- result.unit.declarations.single as ClassDeclaration;
+ result.unit!.declarations.single as ClassDeclaration;
FieldDeclaration fieldDeclaration =
class_.members.single as FieldDeclaration;
var field = fieldDeclaration.fields.variables[0];
@@ -80,7 +79,7 @@
''');
var result = await resolveTestFile();
ClassDeclaration class_ =
- result.unit.declarations.single as ClassDeclaration;
+ result.unit!.declarations.single as ClassDeclaration;
FieldDeclaration fieldDeclaration =
class_.members.single as FieldDeclaration;
var type = fieldDeclaration.fields.type;
@@ -96,7 +95,7 @@
''');
var result = await resolveTestFile();
ClassDeclaration class_ =
- result.unit.declarations.single as ClassDeclaration;
+ result.unit!.declarations.single as ClassDeclaration;
var getter = class_.members.single;
expect(InfoBuilder.buildEnclosingMemberDescription(getter),
equals("the getter 'C.aaa'"));
@@ -110,7 +109,7 @@
''');
var result = await resolveTestFile();
ClassDeclaration class_ =
- result.unit.declarations.single as ClassDeclaration;
+ result.unit!.declarations.single as ClassDeclaration;
var method = class_.members.single;
expect(InfoBuilder.buildEnclosingMemberDescription(method),
equals("the method 'C.aaa'"));
@@ -124,7 +123,7 @@
''');
var result = await resolveTestFile();
ClassDeclaration class_ =
- result.unit.declarations.single as ClassDeclaration;
+ result.unit!.declarations.single as ClassDeclaration;
var operator = class_.members.single;
expect(InfoBuilder.buildEnclosingMemberDescription(operator),
equals("the operator 'C.=='"));
@@ -138,7 +137,7 @@
''');
var result = await resolveTestFile();
ClassDeclaration class_ =
- result.unit.declarations.single as ClassDeclaration;
+ result.unit!.declarations.single as ClassDeclaration;
var setter = class_.members.single;
expect(InfoBuilder.buildEnclosingMemberDescription(setter),
equals("the setter 'C.aaa='"));
@@ -152,7 +151,7 @@
''');
var result = await resolveTestFile();
ExtensionDeclaration extension_ =
- result.unit.declarations.single as ExtensionDeclaration;
+ result.unit!.declarations.single as ExtensionDeclaration;
var method = extension_.members.single;
expect(InfoBuilder.buildEnclosingMemberDescription(method),
equals("the method 'E.aaa'"));
@@ -166,7 +165,7 @@
''');
var result = await resolveTestFile();
ExtensionDeclaration extension_ =
- result.unit.declarations.single as ExtensionDeclaration;
+ result.unit!.declarations.single as ExtensionDeclaration;
var method = extension_.members.single;
expect(InfoBuilder.buildEnclosingMemberDescription(method),
equals("the method 'aaa' in unnamed extension on List<dynamic>"));
@@ -180,7 +179,7 @@
''');
var result = await resolveTestFile();
MixinDeclaration mixin_ =
- result.unit.declarations.single as MixinDeclaration;
+ result.unit!.declarations.single as MixinDeclaration;
var method = mixin_.members.single;
expect(InfoBuilder.buildEnclosingMemberDescription(method),
equals("the method 'C.aaa'"));
@@ -191,7 +190,7 @@
void aaa(value) {}
''');
var result = await resolveTestFile();
- var function = result.unit.declarations.single;
+ var function = result.unit!.declarations.single;
expect(InfoBuilder.buildEnclosingMemberDescription(function),
equals("the function 'aaa'"));
}
@@ -201,7 +200,7 @@
int get aaa => 7;
''');
var result = await resolveTestFile();
- var getter = result.unit.declarations.single;
+ var getter = result.unit!.declarations.single;
expect(InfoBuilder.buildEnclosingMemberDescription(getter),
equals("the getter 'aaa'"));
}
@@ -211,7 +210,7 @@
void set aaa(value) {}
''');
var result = await resolveTestFile();
- var setter = result.unit.declarations.single;
+ var setter = result.unit!.declarations.single;
expect(InfoBuilder.buildEnclosingMemberDescription(setter),
equals("the setter 'aaa='"));
}
@@ -222,7 +221,7 @@
''');
var result = await resolveTestFile();
TopLevelVariableDeclaration topLevelVariableDeclaration =
- result.unit.declarations.single as TopLevelVariableDeclaration;
+ result.unit!.declarations.single as TopLevelVariableDeclaration;
var variable = topLevelVariableDeclaration.variables.variables[0];
expect(InfoBuilder.buildEnclosingMemberDescription(variable),
equals("the variable 'i'"));
@@ -234,7 +233,7 @@
''');
var result = await resolveTestFile();
TopLevelVariableDeclaration topLevelVariableDeclaration =
- result.unit.declarations.single as TopLevelVariableDeclaration;
+ result.unit!.declarations.single as TopLevelVariableDeclaration;
var type = topLevelVariableDeclaration.variables.type;
expect(InfoBuilder.buildEnclosingMemberDescription(type),
equals("the variable 'i'"));
@@ -245,16 +244,19 @@
class InfoBuilderTest extends NnbdMigrationTestBase {
/// Assert various properties of the given [edit].
bool assertEdit(
- {@required EditDetail edit, int offset, int length, String replacement}) {
+ {required EditDetail? edit,
+ int? offset,
+ int? length,
+ String? replacement}) {
expect(edit, isNotNull);
if (offset != null) {
- expect(edit.offset, offset);
+ expect(edit!.offset, offset);
}
if (length != null) {
- expect(edit.length, length);
+ expect(edit!.length, length);
}
if (replacement != null) {
- expect(edit.replacement, replacement);
+ expect(edit!.replacement, replacement);
}
return true;
}
@@ -399,7 +401,7 @@
}
''');
var operator = '+=';
- var operatorOffset = unit.content.indexOf(operator);
+ var operatorOffset = unit.content!.indexOf(operator);
var region =
unit.regions.where((region) => region.offset == operatorOffset).single;
assertRegion(
@@ -421,7 +423,7 @@
}
''');
var operator = '+=';
- var operatorOffset = unit.content.indexOf(operator);
+ var operatorOffset = unit.content!.indexOf(operator);
var region =
unit.regions.where((region) => region.offset == operatorOffset).single;
assertRegion(
@@ -439,7 +441,7 @@
'int f(String s) => s == null /* == false */ ? 0 : s.length;',
warnOnWeakCode: true);
var insertedComment = '/* == false */';
- var insertedCommentOffset = unit.content.indexOf(insertedComment);
+ var insertedCommentOffset = unit.content!.indexOf(insertedComment);
var region = unit.regions
.where((region) => region.offset == insertedCommentOffset)
.single;
@@ -470,7 +472,7 @@
}
''', warnOnWeakCode: true);
var insertedComment = '/* == false */';
- var insertedCommentOffset = unit.content.indexOf(insertedComment);
+ var insertedCommentOffset = unit.content!.indexOf(insertedComment);
var region = unit.regions
.where((region) => region.offset == insertedCommentOffset)
.single;
@@ -489,7 +491,7 @@
'int f(String s) => s != null /* == true */ ? s.length : 0;',
warnOnWeakCode: true);
var insertedComment = '/* == true */';
- var insertedCommentOffset = unit.content.indexOf(insertedComment);
+ var insertedCommentOffset = unit.content!.indexOf(insertedComment);
var region = unit.regions
.where((region) => region.offset == insertedCommentOffset)
.single;
@@ -520,7 +522,7 @@
}
''', warnOnWeakCode: true);
var insertedComment = '/* == true */';
- var insertedCommentOffset = unit.content.indexOf(insertedComment);
+ var insertedCommentOffset = unit.content!.indexOf(insertedComment);
var region = unit.regions
.where((region) => region.offset == insertedCommentOffset)
.single;
@@ -655,7 +657,7 @@
traces: isNotEmpty);
var traceDescriptionToOffset = {
for (var trace in region.traces)
- trace.description: trace.entries[0].target.offset
+ trace.description: trace.entries[0].target!.offset
};
expect(traceDescriptionToOffset, {
'Nullability reason': content.indexOf('List<int/*?*/>/*?*/'),
@@ -763,7 +765,7 @@
}
''');
var operator = '++';
- var operatorOffset = unit.content.indexOf(operator);
+ var operatorOffset = unit.content!.indexOf(operator);
var region =
unit.regions.where((region) => region.offset == operatorOffset).single;
assertRegion(
@@ -785,7 +787,7 @@
}
''');
var operator = '++';
- var operatorOffset = unit.content.indexOf(operator);
+ var operatorOffset = unit.content!.indexOf(operator);
var region =
unit.regions.where((region) => region.offset == operatorOffset).single;
assertRegion(
@@ -996,7 +998,7 @@
int f(int/*!*/ x, int y) => x ??= y;
''', warnOnWeakCode: false, removeViaComments: false);
var codeToRemove = ' ??= y';
- var removalOffset = unit.content.indexOf(codeToRemove);
+ var removalOffset = unit.content!.indexOf(codeToRemove);
var region =
unit.regions.where((region) => region.offset == removalOffset).single;
assertRegion(
@@ -1016,7 +1018,7 @@
int f(int/*!*/ x, int y) => x ??= y;
''', warnOnWeakCode: true);
var operator = '??=';
- var operatorOffset = unit.content.indexOf(operator);
+ var operatorOffset = unit.content!.indexOf(operator);
var region =
unit.regions.where((region) => region.offset == operatorOffset).single;
assertRegion(
@@ -1035,7 +1037,7 @@
int f(String s) => s?.length;
''', warnOnWeakCode: true);
var question = '?';
- var questionOffset = unit.content.indexOf(question);
+ var questionOffset = unit.content!.indexOf(question);
var region =
unit.regions.where((region) => region.offset == questionOffset).single;
assertRegion(
@@ -1137,7 +1139,7 @@
expect(trace.description, 'Nullability reason');
var entries = trace.entries;
expect(entries, hasLength(2));
- assertTraceEntry(unit, entries[0], 'A.m', unit.content.indexOf('int?'),
+ assertTraceEntry(unit, entries[0], 'A.m', unit.content!.indexOf('int?'),
contains('A.m (test.dart:2:3)'));
}
@@ -1240,14 +1242,18 @@
}
''');
var region = unit.regions
- .where((info) => info.offset == unit.content.indexOf('? i) {}'))
+ .where((info) => info.offset == unit.content!.indexOf('? i) {}'))
.single;
expect(region.traces, hasLength(1));
var trace = region.traces.single;
expect(trace.description, 'Nullability reason');
var entries = trace.entries;
- assertTraceEntry(unit, entries[0], 'C.named',
- unit.content.indexOf('int? i) {}'), contains('parameter 0 of C.named'));
+ assertTraceEntry(
+ unit,
+ entries[0],
+ 'C.named',
+ unit.content!.indexOf('int? i) {}'),
+ contains('parameter 0 of C.named'));
}
Future<void> test_trace_constructor_unnamed() async {
@@ -1267,7 +1273,7 @@
}
''');
var region = unit.regions
- .where((info) => info.offset == unit.content.indexOf('? i) {}'))
+ .where((info) => info.offset == unit.content!.indexOf('? i) {}'))
.single;
expect(region.traces, hasLength(1));
var trace = region.traces.single;
@@ -1277,7 +1283,7 @@
unit,
entries[0],
'C.<unnamed>',
- unit.content.indexOf('int? i) {}'),
+ unit.content!.indexOf('int? i) {}'),
contains('parameter 0 of C.<unnamed>'));
}
@@ -1293,7 +1299,7 @@
''');
var region = unit.regions
.where(
- (regionInfo) => regionInfo.offset == unit.content.indexOf('/* if'))
+ (regionInfo) => regionInfo.offset == unit.content!.indexOf('/* if'))
.single;
expect(region.traces, hasLength(1));
var trace = region.traces.single;
@@ -1301,10 +1307,10 @@
var entries = trace.entries;
expect(entries, hasLength(2));
// Entry 0 is the nullability of f's argument
- assertTraceEntry(unit, entries[0], 'f', unit.content.indexOf('int'),
+ assertTraceEntry(unit, entries[0], 'f', unit.content!.indexOf('int'),
contains('parameter 0 of f'));
// Entry 1 is the edge from f's argument to never, due to the `/*!*/` hint.
- assertTraceEntry(unit, entries[1], 'f', unit.content.indexOf('int'),
+ assertTraceEntry(unit, entries[1], 'f', unit.content!.indexOf('int'),
'explicitly hinted to be non-nullable');
}
@@ -1325,7 +1331,7 @@
}
''');
var region = unit.regions
- .where((info) => info.offset == unit.content.indexOf('? i) {}'))
+ .where((info) => info.offset == unit.content!.indexOf('? i) {}'))
.single;
expect(region.traces, hasLength(1));
var trace = region.traces.single;
@@ -1335,7 +1341,7 @@
unit,
entries[0],
'<unnamed extension>.m',
- unit.content.indexOf('int? i) {}'),
+ unit.content!.indexOf('int? i) {}'),
contains('parameter 0 of <unnamed>.m'));
}
@@ -1359,7 +1365,7 @@
''');
var region = unit.regions
.where((regionInfo) =>
- regionInfo.offset == unit.content.indexOf('? i) {} // f'))
+ regionInfo.offset == unit.content!.indexOf('? i) {} // f'))
.single;
expect(region.traces, hasLength(1));
var trace = region.traces.single;
@@ -1368,7 +1374,7 @@
expect(entries, hasLength(6));
// Entry 0 is the nullability of f's argument
assertTraceEntry(unit, entries[0], 'f',
- unit.content.indexOf('int? i) {} // f'), contains('parameter 0 of f'),
+ unit.content!.indexOf('int? i) {} // f'), contains('parameter 0 of f'),
hintActions: {
HintActionKind.addNullableHint,
HintActionKind.addNonNullableHint
@@ -1377,24 +1383,24 @@
// Entry 1 is the edge from g's argument to f's argument, due to g's call to
// f.
assertTraceEntry(
- unit, entries[1], 'g', unit.content.indexOf('i);'), 'data flow');
+ unit, entries[1], 'g', unit.content!.indexOf('i);'), 'data flow');
// Entry 2 is the nullability of g's argument
assertTraceEntry(unit, entries[2], 'g',
- unit.content.indexOf('int? i) { // g'), contains('parameter 0 of g'),
+ unit.content!.indexOf('int? i) { // g'), contains('parameter 0 of g'),
hintActions: {
HintActionKind.addNullableHint,
HintActionKind.addNonNullableHint
});
// Entry 3 is the edge from null to g's argument, due to h's call to g.
assertTraceEntry(
- unit, entries[3], 'h', unit.content.indexOf('null'), 'data flow');
+ unit, entries[3], 'h', unit.content!.indexOf('null'), 'data flow');
// Entry 4 is the nullability of the null literal.
- assertTraceEntry(unit, entries[4], 'h', unit.content.indexOf('null'),
+ assertTraceEntry(unit, entries[4], 'h', unit.content!.indexOf('null'),
contains('null literal'));
// Entry 5 is the edge from always to null.
// TODO(paulberry): this edge provides no additional useful information and
// shouldn't be included in the trace.
- assertTraceEntry(unit, entries[5], 'h', unit.content.indexOf('null'),
+ assertTraceEntry(unit, entries[5], 'h', unit.content!.indexOf('null'),
'literal expression');
}
@@ -1402,7 +1408,8 @@
var unit = await buildInfoForSingleTestFile('int f(int/*?*/ i) => i + 1;',
migratedContent: 'int f(int/*?*/ i) => i! + 1;');
var region = unit.regions
- .where((regionInfo) => regionInfo.offset == unit.content.indexOf('! +'))
+ .where(
+ (regionInfo) => regionInfo.offset == unit.content!.indexOf('! +'))
.single;
expect(region.traces, hasLength(1));
var trace = region.traces.single;
@@ -1410,12 +1417,12 @@
var entries = trace.entries;
expect(entries, hasLength(2));
// Entry 0 is the nullability of the type of i.
- assertTraceEntry(unit, entries[0], 'f', unit.content.indexOf('int/*?*/'),
+ assertTraceEntry(unit, entries[0], 'f', unit.content!.indexOf('int/*?*/'),
contains('parameter 0 of f'));
// Entry 1 is the edge from always to the type of i.
// TODO(paulberry): this edge provides no additional useful information and
// shouldn't be included in the trace.
- assertTraceEntry(unit, entries[1], 'f', unit.content.indexOf('int/*?*/'),
+ assertTraceEntry(unit, entries[1], 'f', unit.content!.indexOf('int/*?*/'),
'explicitly hinted to be nullable');
}
@@ -1442,7 +1449,7 @@
}
''');
var region = unit.regions
- .where((regionInfo) => regionInfo.offset == unit.content.indexOf('!)'))
+ .where((regionInfo) => regionInfo.offset == unit.content!.indexOf('!)'))
.single;
expect(region.traces, hasLength(2));
// Trace 0 is the nullability reason; we don't care about that right now.
@@ -1453,16 +1460,16 @@
expect(entries, hasLength(4));
// Entry 0 is the nullability of g's argument
assertTraceEntry(unit, entries[0], 'g',
- unit.content.indexOf('int i) { // g'), contains('parameter 0 of g'));
+ unit.content!.indexOf('int i) { // g'), contains('parameter 0 of g'));
// Entry 1 is the edge from g's argument to f's argument, due to g's call to
// f.
assertTraceEntry(unit, entries[1], 'g',
- unit.content.indexOf('i); // call f'), 'data flow');
+ unit.content!.indexOf('i); // call f'), 'data flow');
// Entry 2 is the nullability of f's argument
assertTraceEntry(unit, entries[2], 'f',
- unit.content.indexOf('int i) { // f'), contains('parameter 0 of f'));
+ unit.content!.indexOf('int i) { // f'), contains('parameter 0 of f'));
// Entry 3 is the edge f's argument to never, due to the assert.
- assertTraceEntry(unit, entries[3], 'f', unit.content.indexOf('assert'),
+ assertTraceEntry(unit, entries[3], 'f', unit.content!.indexOf('assert'),
'value asserted to be non-null');
}
@@ -1471,14 +1478,14 @@
migratedContent: 'int f(int/*?*/ i) => i/*!*/;');
var region = unit.regions
.where(
- (regionInfo) => regionInfo.offset == unit.content.indexOf('/*!*/'))
+ (regionInfo) => regionInfo.offset == unit.content!.indexOf('/*!*/'))
.single;
expect(region.traces, hasLength(1));
var trace = region.traces.single;
expect(trace.description, 'Reason');
expect(trace.entries, hasLength(1));
assertTraceEntry(unit, trace.entries.single, 'f',
- unit.content.indexOf('i/*!*/'), 'Null check hint');
+ unit.content!.indexOf('i/*!*/'), 'Null check hint');
}
Future<void> test_trace_refers_to_variable_initializer() async {
@@ -1494,7 +1501,8 @@
}
''');
var region = unit.regions
- .where((regionInfo) => regionInfo.offset == unit.content.indexOf('? y'))
+ .where(
+ (regionInfo) => regionInfo.offset == unit.content!.indexOf('? y'))
.single;
expect(region.traces, hasLength(1));
var trace = region.traces.single;
@@ -1502,30 +1510,30 @@
var entries = trace.entries;
expect(entries, hasLength(8));
// Entry 0 is the nullability of y
- assertTraceEntry(unit, entries[0], 'f.y', unit.content.indexOf('int? y'),
+ assertTraceEntry(unit, entries[0], 'f.y', unit.content!.indexOf('int? y'),
contains('f.y'));
// Entry 1 is the edge from the list element type of x to y, due to array
// indexing.
- assertTraceEntry(unit, entries[1], 'f.y', unit.content.indexOf('x[0]'),
+ assertTraceEntry(unit, entries[1], 'f.y', unit.content!.indexOf('x[0]'),
contains('data flow'));
// Entry 2 is the nullability of the implicit list element type of x
- assertTraceEntry(unit, entries[2], 'f.x', unit.content.indexOf('x ='),
+ assertTraceEntry(unit, entries[2], 'f.x', unit.content!.indexOf('x ='),
contains('type argument 0 of f.x'));
// Entry 3 is the edge from the explicit list element type on the RHS of x
// to the implicit list element type on the LHS of x
- assertTraceEntry(unit, entries[3], 'f.x', unit.content.indexOf('<int?>[i]'),
- contains('data flow'));
+ assertTraceEntry(unit, entries[3], 'f.x',
+ unit.content!.indexOf('<int?>[i]'), contains('data flow'));
// Entry 4 is the explicit list element type on the RHS of x
- assertTraceEntry(unit, entries[4], 'f.x', unit.content.indexOf('int?>[i]'),
+ assertTraceEntry(unit, entries[4], 'f.x', unit.content!.indexOf('int?>[i]'),
contains('list element type'));
// Entry 5 is the edge from the parameter i to the list literal
- assertTraceEntry(unit, entries[5], 'f.x', unit.content.indexOf('i]'),
+ assertTraceEntry(unit, entries[5], 'f.x', unit.content!.indexOf('i]'),
contains('data flow'));
// Entry 6 is the nullability of the parameter i
- assertTraceEntry(unit, entries[6], 'f', unit.content.indexOf('int/*?*/'),
+ assertTraceEntry(unit, entries[6], 'f', unit.content!.indexOf('int/*?*/'),
contains('parameter 0 of f'));
// Entry 7 is the edge due to the explicit /*?*/ hint
- assertTraceEntry(unit, entries[7], 'f', unit.content.indexOf('int/*?*/'),
+ assertTraceEntry(unit, entries[7], 'f', unit.content!.indexOf('int/*?*/'),
contains('explicitly hinted to be nullable'));
}
@@ -1546,7 +1554,7 @@
String/*!*/ y = x[0]!;
''');
var region = unit.regions
- .where((regionInfo) => regionInfo.offset == unit.content.indexOf('!;'))
+ .where((regionInfo) => regionInfo.offset == unit.content!.indexOf('!;'))
.single;
// The "why nullable" node associated with adding the `!` is a substitution
// node, and we don't currently generate a trace for a substitution node.
@@ -1608,7 +1616,8 @@
var unit = await buildInfoForSingleTestFile('int i = 0;',
migratedContent: 'int i = 0;');
var region = unit.regions
- .where((regionInfo) => regionInfo.offset == unit.content.indexOf(' i'))
+ .where(
+ (regionInfo) => regionInfo.offset == unit.content!.indexOf(' i'))
.single;
expect(region.length, 1);
expect(region.lineNumber, 1);
@@ -1619,7 +1628,7 @@
expect(trace.description, 'Non-nullability reason');
var entries = trace.entries;
expect(entries, hasLength(1));
- assertTraceEntry(unit, entries[0], 'i', unit.content.indexOf('int'),
+ assertTraceEntry(unit, entries[0], 'i', unit.content!.indexOf('int'),
'No reason found to make nullable');
expect(region.kind, NullabilityFixKind.typeNotMadeNullable);
}
diff --git a/pkg/nnbd_migration/test/front_end/mocks.dart b/pkg/nnbd_migration/test/front_end/mocks.dart
index de744b6..d09c799 100644
--- a/pkg/nnbd_migration/test/front_end/mocks.dart
+++ b/pkg/nnbd_migration/test/front_end/mocks.dart
@@ -24,7 +24,7 @@
@override
Description describeMismatch(
item, Description mismatchDescription, Map matchState, bool verbose) {
- Response response = item as Response;
+ Response? response = item as Response?;
if (response == null) {
mismatchDescription.add('is null response');
} else {
@@ -40,7 +40,7 @@
@override
bool matches(item, Map matchState) {
- Response response = item as Response;
+ Response? response = item as Response?;
return response != null && response.id == _id && response.error == null;
}
}
diff --git a/pkg/nnbd_migration/test/front_end/navigation_tree_renderer_test.dart b/pkg/nnbd_migration/test/front_end/navigation_tree_renderer_test.dart
index 9aa39c1..54a5e39 100644
--- a/pkg/nnbd_migration/test/front_end/navigation_tree_renderer_test.dart
+++ b/pkg/nnbd_migration/test/front_end/navigation_tree_renderer_test.dart
@@ -24,7 +24,7 @@
@reflectiveTest
class NavigationTreeRendererTest extends NnbdMigrationTestBase {
- PathMapper pathMapper;
+ PathMapper? pathMapper;
/// Render the navigation tree view for [files].
Future<List<NavigationTreeNode>> renderNavigationTree(
@@ -66,15 +66,15 @@
isNavigationTreeDirectoryNode.named('lib').havingSubtree([
isNavigationTreeDirectoryNode.named('src').havingSubtree([
isNavigationTreeFileNode.havingHref(
- '$projectPath/lib/src/b.dart', pathMapper)
+ '$projectPath/lib/src/b.dart', pathMapper!)
]),
isNavigationTreeFileNode.havingHref(
- '$projectPath/lib/a.dart', pathMapper)
+ '$projectPath/lib/a.dart', pathMapper!)
]));
var toolNode = response[1] as NavigationTreeFileNode;
expect(
- toolNode.href, pathMapper.map(convertPath('$projectPath/tool.dart')));
+ toolNode.href, pathMapper!.map(convertPath('$projectPath/tool.dart')));
}
Future<void> test_containsMultipleLinks_multipleDepths() async {
@@ -131,11 +131,11 @@
isNavigationTreeFileNode
.named('a.dart')
.havingPath(convertPath('lib/a.dart'))
- .havingHref('$projectPath/lib/a.dart', pathMapper),
+ .havingHref('$projectPath/lib/a.dart', pathMapper!),
isNavigationTreeFileNode
.named('b.dart')
.havingPath(convertPath('lib/b.dart'))
- .havingHref('$projectPath/lib/b.dart', pathMapper)
+ .havingHref('$projectPath/lib/b.dart', pathMapper!)
]));
}
@@ -174,7 +174,7 @@
isNavigationTreeFileNode
.named('a.dart')
.havingPath(convertPath('lib/src/a.dart'))
- .havingHref('$projectPath/lib/src/a.dart', pathMapper)
+ .havingHref('$projectPath/lib/src/a.dart', pathMapper!)
])
]));
}
@@ -188,7 +188,7 @@
var aNode = response[0] as NavigationTreeFileNode;
expect(aNode.name, 'a.dart');
expect(aNode.path, 'a.dart');
- expect(aNode.href, pathMapper.map(convertPath('$projectPath/a.dart')));
+ expect(aNode.href, pathMapper!.map(convertPath('$projectPath/a.dart')));
}
Future<void> test_migrationStatus_directory_allMigrated() async {
@@ -198,10 +198,10 @@
});
var libNode = response[0] as NavigationTreeDirectoryNode;
- ((libNode.subtree[0] as NavigationTreeDirectoryNode).subtree[0]
+ ((libNode.subtree![0] as NavigationTreeDirectoryNode).subtree![0]
as NavigationTreeFileNode)
.migrationStatus = UnitMigrationStatus.alreadyMigrated;
- (libNode.subtree[1] as NavigationTreeFileNode).migrationStatus =
+ (libNode.subtree![1] as NavigationTreeFileNode).migrationStatus =
UnitMigrationStatus.alreadyMigrated;
expect(
libNode,
@@ -243,10 +243,10 @@
});
var libNode = response[0] as NavigationTreeDirectoryNode;
- ((libNode.subtree[0] as NavigationTreeDirectoryNode).subtree[0]
+ ((libNode.subtree![0] as NavigationTreeDirectoryNode).subtree![0]
as NavigationTreeFileNode)
.migrationStatus = UnitMigrationStatus.optingOut;
- (libNode.subtree[1] as NavigationTreeFileNode).migrationStatus =
+ (libNode.subtree![1] as NavigationTreeFileNode).migrationStatus =
UnitMigrationStatus.optingOut;
expect(
libNode,
@@ -269,8 +269,8 @@
});
var libNode = response[0] as NavigationTreeDirectoryNode;
- var libSrcNode = libNode.subtree[0] as NavigationTreeDirectoryNode;
- (libSrcNode.subtree[0] as NavigationTreeFileNode).migrationStatus =
+ var libSrcNode = libNode.subtree![0] as NavigationTreeDirectoryNode;
+ (libSrcNode.subtree![0] as NavigationTreeFileNode).migrationStatus =
UnitMigrationStatus.optingOut;
expect(
libNode,
@@ -293,10 +293,10 @@
});
var libNode = response[0] as NavigationTreeDirectoryNode;
- var libSrcNode = libNode.subtree[0] as NavigationTreeDirectoryNode;
- (libSrcNode.subtree[0] as NavigationTreeFileNode).migrationStatus =
+ var libSrcNode = libNode.subtree![0] as NavigationTreeDirectoryNode;
+ (libSrcNode.subtree![0] as NavigationTreeFileNode).migrationStatus =
UnitMigrationStatus.optingOut;
- (libSrcNode.subtree[1] as NavigationTreeFileNode).migrationStatus =
+ (libSrcNode.subtree![1] as NavigationTreeFileNode).migrationStatus =
UnitMigrationStatus.alreadyMigrated;
expect(
libNode,
diff --git a/pkg/nnbd_migration/test/front_end/nnbd_migration_test_base.dart b/pkg/nnbd_migration/test/front_end/nnbd_migration_test_base.dart
index f9d7b6e..96c987d 100644
--- a/pkg/nnbd_migration/test/front_end/nnbd_migration_test_base.dart
+++ b/pkg/nnbd_migration/test/front_end/nnbd_migration_test_base.dart
@@ -4,7 +4,6 @@
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/source/line_info.dart';
-import 'package:meta/meta.dart';
import 'package:nnbd_migration/instrumentation.dart';
import 'package:nnbd_migration/nnbd_migration.dart';
import 'package:nnbd_migration/src/front_end/dartfix_listener.dart';
@@ -42,15 +41,15 @@
class NnbdMigrationTestBase extends AbstractAnalysisTest {
/// The information produced by the InfoBuilder, or `null` if [buildInfo] has
/// not yet completed.
- Set<UnitInfo> infos;
- NodeMapper nodeMapper;
+ Set<UnitInfo>? infos;
+ NodeMapper? nodeMapper;
/// Assert that some target in [targets] has various properties.
void assertInTargets(
- {@required Iterable<NavigationTarget> targets,
- int offset,
- int length,
- OffsetMapper offsetMapper}) {
+ {required Iterable<NavigationTarget> targets,
+ int? offset,
+ int? length,
+ OffsetMapper? offsetMapper}) {
var failureReasons = [
if (offset != null) 'offset: $offset',
if (length != null) 'length: $length',
@@ -58,7 +57,7 @@
].join(' and ');
offsetMapper ??= OffsetMapper.identity;
expect(targets.any((t) {
- return (offset == null || offset == offsetMapper.map(t.offset)) &&
+ return (offset == null || offset == offsetMapper!.map(t.offset)) &&
(length == null || length == t.length);
}), isTrue, reason: 'Expected one of $targets to contain $failureReasons');
}
@@ -67,9 +66,9 @@
/// provided but no [length] is provided, a default length of `1` will be
/// used.
void assertRegion(
- {@required RegionInfo region,
- int offset,
- int length,
+ {required RegionInfo region,
+ int? offset,
+ int? length,
Object explanation = anything,
Object edits = anything,
Object traces = anything,
@@ -90,10 +89,10 @@
/// `regions[index + 1]`. The expected offsets and lengths are specified
/// separately; everything else is asserted using the same matcher.
void assertRegionPair(List<RegionInfo> regions, int index,
- {int offset1,
- int length1,
- int offset2,
- int length2,
+ {int? offset1,
+ int? length1,
+ int? offset2,
+ int? length2,
Object explanation = anything,
Object edits = anything,
Object traces = anything,
@@ -118,17 +117,13 @@
void assertTraceEntry(UnitInfo unit, TraceEntryInfo entryInfo,
String function, int offset, Object descriptionMatcher,
- {Set<HintActionKind> hintActions}) {
- if (offset == null) {
- expect(entryInfo.target, isNull);
- } else {
- assert(offset >= 0);
- var lineInfo = LineInfo.fromContent(unit.content);
- var expectedLocation = lineInfo.getLocation(offset);
- expect(entryInfo.target.filePath, unit.path);
- expect(entryInfo.target.line, expectedLocation.lineNumber);
- expect(unit.offsetMapper.map(entryInfo.target.offset), offset);
- }
+ {Set<HintActionKind>? hintActions}) {
+ assert(offset >= 0);
+ var lineInfo = LineInfo.fromContent(unit.content!);
+ var expectedLocation = lineInfo.getLocation(offset);
+ expect(entryInfo.target!.filePath, unit.path);
+ expect(entryInfo.target!.line, expectedLocation.lineNumber);
+ expect(unit.offsetMapper.map(entryInfo.target!.offset), offset);
expect(entryInfo.function, function);
expect(entryInfo.description, descriptionMatcher);
if (hintActions != null) {
@@ -143,11 +138,11 @@
traceEntry.hintActions);
expect(actionsByKind, hasLength(expectedHints.length));
for (final expectedHint in expectedHints) {
- final action = actionsByKind[expectedHint];
+ final action = actionsByKind[expectedHint]!;
expect(action, isNotNull);
- final node = nodeMapper.nodeForId(action.nodeId);
+ final node = nodeMapper!.nodeForId(action.nodeId)!;
expect(node, isNotNull);
- expect(unit.offsetMapper.map(node.codeReference.offset), nodeOffset);
+ expect(unit.offsetMapper.map(node.codeReference!.offset), nodeOffset);
}
}
@@ -156,10 +151,10 @@
/// The information is stored in [infos].
Future<void> buildInfo(
{bool removeViaComments = true, bool warnOnWeakCode = false}) async {
- var includedRoot = resourceProvider.pathContext.dirname(testFile);
+ var includedRoot = resourceProvider.pathContext.dirname(testFile!);
await _buildMigrationInfo([testFile],
includedRoot: includedRoot,
- shouldBeMigratedFunction: (String path) => true,
+ shouldBeMigratedFunction: (String? path) => true,
pathsToProcess: [testFile],
removeViaComments: removeViaComments,
warnOnWeakCode: warnOnWeakCode);
@@ -170,7 +165,7 @@
/// Asserts that [originalContent] is migrated to [migratedContent]. Returns
/// the singular UnitInfo which was built.
Future<UnitInfo> buildInfoForSingleTestFile(String originalContent,
- {@required String migratedContent,
+ {required String migratedContent,
bool removeViaComments = true,
bool warnOnWeakCode = false}) async {
addTestFile(originalContent);
@@ -178,8 +173,8 @@
removeViaComments: removeViaComments, warnOnWeakCode: warnOnWeakCode);
// Ignore info for dart:core.
var filteredInfos = [
- for (var info in infos)
- if (!info.path.contains('core.dart')) info
+ for (var info in infos!)
+ if (!info.path!.contains('core.dart')) info
];
expect(filteredInfos, hasLength(1));
var unit = filteredInfos[0];
@@ -192,10 +187,10 @@
///
/// Returns the singular [UnitInfo] which was built.
Future<List<UnitInfo>> buildInfoForTestFiles(Map<String, String> files,
- {@required String includedRoot,
- bool Function(String) shouldBeMigratedFunction,
- Iterable<String> pathsToProcess}) async {
- shouldBeMigratedFunction ??= (String path) => true;
+ {required String? includedRoot,
+ bool Function(String?)? shouldBeMigratedFunction,
+ Iterable<String>? pathsToProcess}) async {
+ shouldBeMigratedFunction ??= (String? path) => true;
var testPaths = <String>[];
files.forEach((String path, String content) {
newFile(path, content: content);
@@ -208,8 +203,8 @@
pathsToProcess: pathsToProcess);
// Ignore info for dart:core.
var filteredInfos = [
- for (var info in infos)
- if (!info.path.contains('core.dart')) info
+ for (var info in infos!)
+ if (!info.path!.contains('core.dart')) info
];
return filteredInfos;
}
@@ -221,14 +216,14 @@
/// Uses the InfoBuilder to build information for files at [testPaths], which
/// should all share a common parent directory, [includedRoot].
- Future<void> _buildMigrationInfo(Iterable<String> testPaths,
- {@required String includedRoot,
- @required bool Function(String) shouldBeMigratedFunction,
- @required Iterable<String> pathsToProcess,
+ Future<void> _buildMigrationInfo(Iterable<String?> testPaths,
+ {required String? includedRoot,
+ required bool Function(String?) shouldBeMigratedFunction,
+ required Iterable<String?> pathsToProcess,
bool removeViaComments = true,
bool warnOnWeakCode = false}) async {
// Compute the analysis results.
- var server = DriverProviderImpl(resourceProvider, driver.analysisContext);
+ var server = DriverProviderImpl(resourceProvider, driver!.analysisContext);
// Run the migration engine.
var listener = DartFixListener(server, ListenerClient());
var instrumentationListener = InstrumentationListener();
@@ -241,7 +236,7 @@
Future<void> _forEachPath(
void Function(ResolvedUnitResult) callback) async {
for (var testPath in testPaths) {
- var result = await driver.currentSession.getResolvedUnit2(testPath)
+ var result = await driver!.currentSession.getResolvedUnit2(testPath!)
as ResolvedUnitResult;
callback(result);
}
diff --git a/pkg/nnbd_migration/test/front_end/region_renderer_test.dart b/pkg/nnbd_migration/test/front_end/region_renderer_test.dart
index d2e4953..a324047 100644
--- a/pkg/nnbd_migration/test/front_end/region_renderer_test.dart
+++ b/pkg/nnbd_migration/test/front_end/region_renderer_test.dart
@@ -23,14 +23,14 @@
class RegionRendererTest extends RegionRendererTestBase {
/// Returns the basename of [testFile], used in traces.
String get _testFileBasename =>
- resourceProvider.pathContext.basename(testFile);
+ resourceProvider.pathContext.basename(testFile!);
Future<void> test_informationalRegion_containsTrace() async {
await buildInfoForSingleTestFile('f(int a) => a.isEven;',
migratedContent: 'f(int a) => a.isEven;');
var response = renderRegion(5);
expect(response.traces, hasLength(1));
- var trace = response.traces[0];
+ var trace = response.traces![0];
expect(trace.description, equals('Non-nullability reason'));
}
@@ -39,7 +39,7 @@
migratedContent: 'f(int a) => a.isEven;');
var response = renderRegion(5);
expect(response.traces, hasLength(1));
- var trace = response.traces[0];
+ var trace = response.traces![0];
expect(trace.entries, hasLength(2));
expect(trace.entries[0].description,
equals('parameter 0 of f ($_testFileBasename:1:3)'));
@@ -51,13 +51,13 @@
migratedContent: 'f(int a) => a.isEven;');
var response = renderRegion(5);
expect(response.traces, hasLength(1));
- var trace = response.traces[0];
+ var trace = response.traces![0];
var entry = trace.entries[0];
expect(entry.link, isNotNull);
- var testFileUriPath = resourceProvider.pathContext.toUri(testFile).path;
- expect(entry.link.href,
+ var testFileUriPath = resourceProvider.pathContext.toUri(testFile!).path;
+ expect(entry.link!.href,
equals('$testFileUriPath?offset=2&line=1&authToken=AUTH_TOKEN'));
- expect(entry.link.path,
+ expect(entry.link!.path,
equals(resourceProvider.pathContext.toUri(_testFileBasename).path));
}
@@ -73,7 +73,7 @@
migratedContent: 'int? a = null;');
var response = renderRegion(3);
expect(response.displayPath, equals(testFile));
- expect(response.uriPath, equals(pathMapper.map(testFile)));
+ expect(response.uriPath, equals(pathMapper!.map(testFile!)));
expect(response.line, equals(1));
}
@@ -82,7 +82,7 @@
migratedContent: 'int? a = null;');
var response = renderRegion(3);
expect(response.traces, hasLength(1));
- var trace = response.traces[0];
+ var trace = response.traces![0];
expect(trace.description, equals('Nullability reason'));
expect(trace.entries, hasLength(4));
expect(trace.entries[0].description, equals('a ($_testFileBasename:1:1)'));
@@ -104,20 +104,20 @@
migratedContent: 'f(int a) => a.isEven;');
var response = renderRegion(5);
expect(response.displayPath, equals(testFile));
- expect(response.uriPath, equals(pathMapper.map(testFile)));
+ expect(response.uriPath, equals(pathMapper!.map(testFile!)));
expect(response.line, equals(1));
}
}
class RegionRendererTestBase extends NnbdMigrationTestBase {
- PathMapper pathMapper;
+ PathMapper? pathMapper;
/// Render the region at [offset], using a [MigrationInfo] which knows only
/// about the library at `infos.single`.
EditDetails renderRegion(int offset) {
var migrationInfo =
MigrationInfo(infos, {}, resourceProvider.pathContext, projectPath);
- var unitInfo = infos.single;
+ var unitInfo = infos!.single;
var region = unitInfo.regionAt(offset);
pathMapper = PathMapper(resourceProvider);
return RegionRenderer(
@@ -159,7 +159,7 @@
expect(response.displayPath,
equals(_switchToDriveD(convertPath('/home/tests/bin/test.dart'))));
expect(response.traces, hasLength(2));
- var trace = response.traces[1];
+ var trace = response.traces![1];
expect(trace.description, equals('Non-nullability reason'));
expect(trace.entries, hasLength(1));
var entry = trace.entries[0];
@@ -172,7 +172,7 @@
var expectedLine =
'\n'.allMatches(coreLibText.substring(0, expectedOffset)).length + 1;
expect(
- entry.link.href,
+ entry.link!.href,
equals('$sdkCoreLibUriPath?'
'offset=$expectedOffset&'
'line=$expectedLine&'
@@ -183,7 +183,7 @@
var expectedLinkPath = resourceProvider.pathContext.style == p.Style.windows
? sdkCoreLibUriPath
: '../../..$sdkCoreLibUriPath';
- expect(entry.link.path, equals(expectedLinkPath));
+ expect(entry.link!.path, equals(expectedLinkPath));
}
/// On Windows, replace the C:\ relative root in [path] with the D:\ relative
diff --git a/pkg/nnbd_migration/test/front_end/unit_renderer_test.dart b/pkg/nnbd_migration/test/front_end/unit_renderer_test.dart
index 9ea346f..6d377eb 100644
--- a/pkg/nnbd_migration/test/front_end/unit_renderer_test.dart
+++ b/pkg/nnbd_migration/test/front_end/unit_renderer_test.dart
@@ -28,7 +28,7 @@
MigrationInfo(infos, {}, resourceProvider.pathContext, packageRoot);
var contents = <FileDetails>[];
- for (var unitInfo in infos) {
+ for (var unitInfo in infos!) {
contents.add(UnitRenderer(unitInfo, migrationInfo,
PathMapper(resourceProvider), 'AUTH_TOKEN')
.render());
@@ -56,7 +56,7 @@
''', warnOnWeakCode: true);
var output = renderUnits()[0];
expect(
- _stripDataAttributes(output.regions),
+ _stripDataAttributes(output.regions!),
contains(
'<span class="region informative-region">/* == false */</span>'));
expect(output.edits.keys,
@@ -83,7 +83,7 @@
''', warnOnWeakCode: true);
var output = renderUnits()[0];
expect(
- _stripDataAttributes(output.regions),
+ _stripDataAttributes(output.regions!),
contains(
'<span class="region informative-region">/* == true */</span>'));
expect(output.edits.keys,
@@ -115,14 +115,14 @@
// The null checks are higher priority than the assertions.
expect(output.edits.keys,
orderedEquals(['1 null check added', '1 type made nullable']));
- var typesMadeNullable = output.edits['1 type made nullable'];
+ var typesMadeNullable = output.edits['1 type made nullable']!;
expect(typesMadeNullable, hasLength(1));
var typeMadeNullable = typesMadeNullable.single;
expect(typeMadeNullable.line, equals(1));
expect(typeMadeNullable.offset, equals(3));
expect(typeMadeNullable.explanation,
equals("Changed type 'int' to be nullable"));
- var nullChecks = output.edits['1 null check added'];
+ var nullChecks = output.edits['1 null check added']!;
expect(nullChecks, hasLength(1));
var nullCheck = nullChecks.single;
expect(nullCheck.line, equals(2));
@@ -247,7 +247,7 @@
''', removeViaComments: false);
var output = renderUnits()[0];
// Strip out URLs and span IDs; they're not being tested here.
- var navContent = output.navigationContent
+ var navContent = output.navigationContent!
.replaceAll(RegExp('href="[^"]*"'), 'href="..."')
.replaceAll(RegExp('id="[^"]*"'), 'id="..."');
expect(navContent, '''
@@ -275,7 +275,7 @@
=> values.firstWherefirstWhereOrNull((i) => i.isEven/* , orElse: () => null */);
''');
var output = renderUnits()[0];
- var regions = output.regions;
+ var regions = output.regions!;
// We're not testing the correctness of the data path, so drop it.
regions = regions.replaceAll(RegExp(' data-path="[^"]*"'), '');
// Split the output into table rows.
@@ -328,7 +328,7 @@
=> values.firstWherefirstWhereOrNull((i) => i.isEven, orElse: () => null);
''');
var output = renderUnits()[0];
- var regions = output.regions;
+ var regions = output.regions!;
// We aren't testing data-offset or data-line behaviors.
regions = regions.replaceAll(RegExp(' data-offset="[^"]*"'), '');
regions = regions.replaceAll(RegExp(' data-line="[^"]*"'), '');
@@ -343,8 +343,8 @@
migratedContent: 'List<String >? a = null;');
var output = renderUnits()[0];
// Strip out URLs which will change; not being tested here.
- var navContent =
- output.navigationContent.replaceAll(RegExp('href=".*?"'), 'href="..."');
+ var navContent = output.navigationContent!
+ .replaceAll(RegExp('href=".*?"'), 'href="..."');
expect(
navContent,
contains(r'<a href="..." class="nav-link">List</a>'
@@ -359,7 +359,7 @@
int f(String s) => s?.length;
''', warnOnWeakCode: true);
var output = renderUnits()[0];
- expect(_stripDataAttributes(output.regions),
+ expect(_stripDataAttributes(output.regions!),
contains('s<span class="region informative-region">?</span>.length'));
expect(
output.edits.keys,
@@ -380,7 +380,7 @@
}
''');
var output = renderUnits()[0];
- var regions = _stripDataAttributes(output.regions);
+ var regions = _stripDataAttributes(output.regions!);
expect(
regions,
contains('final '
@@ -405,7 +405,7 @@
}
''');
var output = renderUnits()[0];
- var regions = _stripDataAttributes(output.regions);
+ var regions = _stripDataAttributes(output.regions!);
expect(
regions,
contains('<span class="region removed-region">var</span>'
@@ -421,7 +421,7 @@
await buildInfoForSingleTestFile('int a = null;',
migratedContent: 'int? a = null;');
var output = renderUnits()[0];
- var regions = _stripDataAttributes(output.regions);
+ var regions = _stripDataAttributes(output.regions!);
expect(regions,
contains('int<span class="region added-region">?</span> a = null;'));
}
@@ -478,7 +478,7 @@
await buildInfoForSingleTestFile('List<String> a = null;',
migratedContent: 'List<String >? a = null;');
var output = renderUnits()[0];
- var regions = _stripDataAttributes(output.regions);
+ var regions = _stripDataAttributes(output.regions!);
expect(
regions,
contains(
@@ -490,7 +490,7 @@
await buildInfoForSingleTestFile('f(List<String> a) => a.join(",");',
migratedContent: 'f(List<String > a) => a.join(",");');
var output = renderUnits()[0];
- var regions = _stripDataAttributes(output.regions);
+ var regions = _stripDataAttributes(output.regions!);
expect(
regions,
contains(
@@ -498,7 +498,8 @@
'><span class="region informative-region"> </span>'));
}
- UnitInfo unit(String path, String content, {List<RegionInfo> regions}) {
+ UnitInfo unit(String path, String content,
+ {required List<RegionInfo> regions}) {
return UnitInfo(convertPath(path))
..content = content
..regions.addAll(regions);
diff --git a/pkg/nnbd_migration/test/instrumentation_test.dart b/pkg/nnbd_migration/test/instrumentation_test.dart
index 605f928..be4ba0d 100644
--- a/pkg/nnbd_migration/test/instrumentation_test.dart
+++ b/pkg/nnbd_migration/test/instrumentation_test.dart
@@ -29,7 +29,7 @@
_InstrumentationClient(this.test);
@override
- void changes(Source source, Map<int, List<AtomicEdit>> changes) {
+ void changes(Source source, Map<int?, List<AtomicEdit>> changes) {
expect(test.changes, isNull);
test.changes = {
for (var entry in changes.entries)
@@ -39,8 +39,8 @@
}
@override
- void explicitTypeNullability(
- Source source, TypeAnnotation typeAnnotation, NullabilityNodeInfo node) {
+ void explicitTypeNullability(Source? source, TypeAnnotation typeAnnotation,
+ NullabilityNodeInfo? node) {
expect(source, test.source);
expect(test.explicitTypeNullability, isNot(contains(typeAnnotation)));
test.explicitTypeNullability[typeAnnotation] = node;
@@ -80,7 +80,7 @@
@override
void implicitReturnType(
- Source source, AstNode node, DecoratedTypeInfo decoratedReturnType) {
+ Source? source, AstNode node, DecoratedTypeInfo? decoratedReturnType) {
expect(source, test.source);
expect(test.implicitReturnType, isNot(contains(node)));
test.implicitReturnType[node] = decoratedReturnType;
@@ -88,7 +88,7 @@
@override
void implicitType(
- Source source, AstNode node, DecoratedTypeInfo decoratedType) {
+ Source? source, AstNode? node, DecoratedTypeInfo decoratedType) {
expect(source, test.source);
expect(test.implicitType, isNot(contains(node)));
test.implicitType[node] = decoratedType;
@@ -96,7 +96,7 @@
@override
void implicitTypeArguments(
- Source source, AstNode node, Iterable<DecoratedTypeInfo> types) {
+ Source? source, AstNode node, Iterable<DecoratedTypeInfo> types) {
expect(source, test.source);
expect(test.implicitTypeArguments, isNot(contains(node)));
test.implicitTypeArguments[node] = types.toList();
@@ -112,9 +112,9 @@
class _InstrumentationTest extends _InstrumentationTestBase {}
abstract class _InstrumentationTestBase extends AbstractContextTest {
- NullabilityNodeInfo always;
+ NullabilityNodeInfo? always;
- final Map<TypeAnnotation, NullabilityNodeInfo> explicitTypeNullability = {};
+ final Map<TypeAnnotation, NullabilityNodeInfo?> explicitTypeNullability = {};
final Map<Element, DecoratedTypeInfo> externalDecoratedType = {};
@@ -123,21 +123,21 @@
final List<EdgeInfo> edges = [];
- Map<int, List<AtomicEdit>> changes;
+ Map<int?, List<AtomicEdit>>? changes;
- final Map<AstNode, DecoratedTypeInfo> implicitReturnType = {};
+ final Map<AstNode, DecoratedTypeInfo?> implicitReturnType = {};
- final Map<AstNode, DecoratedTypeInfo> implicitType = {};
+ final Map<AstNode?, DecoratedTypeInfo> implicitType = {};
final Map<AstNode, List<DecoratedTypeInfo>> implicitTypeArguments = {};
- NullabilityNodeInfo never;
+ NullabilityNodeInfo? never;
final Map<EdgeInfo, EdgeOriginInfo> edgeOrigin = {};
- FindNode findNode;
+ late FindNode findNode;
- Source source;
+ Source? source;
Future<void> analyze(String content,
{bool removeViaComments = false, bool warnOnWeakCode = true}) async {
@@ -150,8 +150,8 @@
warnOnWeakCode: warnOnWeakCode);
var result =
await session.getResolvedUnit2(sourcePath) as ResolvedUnitResult;
- source = result.unit.declaredElement.source;
- findNode = FindNode(content, result.unit);
+ source = result.unit!.declaredElement!.source;
+ findNode = FindNode(content, result.unit!);
migration.prepareInput(result);
expect(migration.unmigratedDependencies, isEmpty);
migration.processInput(result);
@@ -161,7 +161,7 @@
void assertEdit(AtomicEdit edit,
{dynamic description = anything, dynamic fixReasons = anything}) {
- var info = edit.info;
+ var info = edit.info!;
expect(info.description, description);
expect(info.fixReasons, fixReasons);
}
@@ -172,9 +172,11 @@
int y = null;
''';
await analyze(content);
- expect(explicitTypeNullability[findNode.typeAnnotation('int x')].isNullable,
+ expect(
+ explicitTypeNullability[findNode.typeAnnotation('int x')]!.isNullable,
false);
- expect(explicitTypeNullability[findNode.typeAnnotation('int y')].isNullable,
+ expect(
+ explicitTypeNullability[findNode.typeAnnotation('int y')]!.isNullable,
true);
}
@@ -185,8 +187,8 @@
}
''');
expect(
- externalDecoratedType[findNode.simple('print').staticElement]
- .type
+ externalDecoratedType[findNode.simple('print').staticElement!]!
+ .type!
.getDisplayString(withNullability: false),
'void Function(Object)');
}
@@ -199,8 +201,8 @@
var pointElement = findNode.simple('Point').staticElement as ClassElement;
var pointElementTypeParameter = pointElement.typeParameters[0];
expect(
- externalDecoratedTypeParameterBound[pointElementTypeParameter]
- .type
+ externalDecoratedTypeParameterBound[pointElementTypeParameter]!
+ .type!
.getDisplayString(withNullability: false),
'num');
}
@@ -212,13 +214,13 @@
var listElement = findNode.simple('List').staticElement as ClassElement;
var listElementTypeParameter = listElement.typeParameters[0];
var typeParameterBoundNode =
- externalDecoratedTypeParameterBound[listElementTypeParameter].node;
+ externalDecoratedTypeParameterBound[listElementTypeParameter]!.node;
var edge = edges
.where((e) =>
e.sourceNode == always &&
e.destinationNode == typeParameterBoundNode)
.single;
- var origin = edgeOrigin[edge];
+ var origin = edgeOrigin[edge]!;
expect(origin.kind, EdgeOriginKind.alwaysNullableType);
expect(origin.element, same(listElementTypeParameter));
expect(origin.source, null);
@@ -231,8 +233,8 @@
var intAnnotation = findNode.typeAnnotation('int');
var intPos = content.indexOf('int');
var commentPos = content.indexOf('/*');
- expect(changes.keys, unorderedEquals([intPos, commentPos]));
- assertEdit(changes[intPos].single,
+ expect(changes!.keys, unorderedEquals([intPos, commentPos]));
+ assertEdit(changes![intPos]!.single,
description: NullabilityFixDescription.addRequired(null, '_f', 'i'),
fixReasons: {
FixReasonTarget.root: same(explicitTypeNullability[intAnnotation])
@@ -245,8 +247,8 @@
var intAnnotation = findNode.typeAnnotation('int');
var intPos = content.indexOf('int');
var commentPos = content.indexOf('/*');
- expect(changes.keys, unorderedEquals([intPos, commentPos]));
- assertEdit(changes[intPos].single,
+ expect(changes!.keys, unorderedEquals([intPos, commentPos]));
+ assertEdit(changes![intPos]!.single,
description: NullabilityFixDescription.addRequired('C', '_f', 'i'),
fixReasons: {
FixReasonTarget.root: same(explicitTypeNullability[intAnnotation])
@@ -266,14 +268,14 @@
var commentPos = content.indexOf('/*');
var ifPos = content.indexOf('if');
var afterReturnPos = content.indexOf('i;') + 2;
- expect(changes.keys, unorderedEquals([commentPos, ifPos, afterReturnPos]));
+ expect(changes!.keys, unorderedEquals([commentPos, ifPos, afterReturnPos]));
var expectedFixReasons = {
FixReasonTarget.root: same(explicitTypeNullability[intAnnotation])
};
- assertEdit(changes[ifPos].single,
+ assertEdit(changes![ifPos]!.single,
description: NullabilityFixDescription.discardCondition,
fixReasons: expectedFixReasons);
- assertEdit(changes[afterReturnPos].single,
+ assertEdit(changes![afterReturnPos]!.single,
description: NullabilityFixDescription.discardCondition,
fixReasons: expectedFixReasons);
}
@@ -288,8 +290,8 @@
var intAnnotation = findNode.typeAnnotation('int');
var commentPos = content.indexOf('/*');
var ifPos = content.indexOf('if');
- expect(changes.keys, unorderedEquals([commentPos, ifPos]));
- assertEdit(changes[ifPos].single,
+ expect(changes!.keys, unorderedEquals([commentPos, ifPos]));
+ assertEdit(changes![ifPos]!.single,
description: NullabilityFixDescription.discardCondition,
fixReasons: {
FixReasonTarget.root: same(explicitTypeNullability[intAnnotation])
@@ -311,14 +313,14 @@
var commentPos = content.indexOf('/*');
var ifPos = content.indexOf('if');
var afterReturnPos = content.indexOf('i;') + 2;
- expect(changes.keys, unorderedEquals([commentPos, ifPos, afterReturnPos]));
+ expect(changes!.keys, unorderedEquals([commentPos, ifPos, afterReturnPos]));
var expectedFixReasons = {
FixReasonTarget.root: same(explicitTypeNullability[intAnnotation])
};
- assertEdit(changes[ifPos].single,
+ assertEdit(changes![ifPos]!.single,
description: NullabilityFixDescription.discardCondition,
fixReasons: expectedFixReasons);
- assertEdit(changes[afterReturnPos].single,
+ assertEdit(changes![afterReturnPos]!.single,
description: NullabilityFixDescription.discardElse,
fixReasons: expectedFixReasons);
}
@@ -335,8 +337,8 @@
var intAnnotation = findNode.typeAnnotation('int');
var commentPos = content.indexOf('/*');
var bodyPos = content.indexOf('i) {') + 4;
- expect(changes.keys, unorderedEquals([commentPos, bodyPos]));
- assertEdit(changes[bodyPos].single,
+ expect(changes!.keys, unorderedEquals([commentPos, bodyPos]));
+ assertEdit(changes![bodyPos]!.single,
description: NullabilityFixDescription.discardIf,
fixReasons: {
FixReasonTarget.root: same(explicitTypeNullability[intAnnotation])
@@ -358,14 +360,14 @@
var commentPos = content.indexOf('/*');
var ifPos = content.indexOf('if');
var afterReturnPos = content.indexOf('i;') + 2;
- expect(changes.keys, unorderedEquals([commentPos, ifPos, afterReturnPos]));
+ expect(changes!.keys, unorderedEquals([commentPos, ifPos, afterReturnPos]));
var expectedFixReasons = {
FixReasonTarget.root: same(explicitTypeNullability[intAnnotation])
};
- assertEdit(changes[ifPos].single,
+ assertEdit(changes![ifPos]!.single,
description: NullabilityFixDescription.discardThen,
fixReasons: expectedFixReasons);
- assertEdit(changes[afterReturnPos].single,
+ assertEdit(changes![afterReturnPos]!.single,
description: NullabilityFixDescription.discardThen,
fixReasons: expectedFixReasons);
}
@@ -382,8 +384,8 @@
var intAnnotation = findNode.typeAnnotation('int');
var commentPos = content.indexOf('/*');
var bodyPos = content.indexOf('i) {') + 4;
- expect(changes.keys, unorderedEquals([commentPos, bodyPos]));
- assertEdit(changes[bodyPos].single,
+ expect(changes!.keys, unorderedEquals([commentPos, bodyPos]));
+ assertEdit(changes![bodyPos]!.single,
description: NullabilityFixDescription.discardIf,
fixReasons: {
FixReasonTarget.root: same(explicitTypeNullability[intAnnotation])
@@ -405,10 +407,10 @@
}
''');
var yUsage = findNode.simple('y);');
- var edit = changes[yUsage.end].single;
+ var edit = changes![yUsage.end]!.single;
expect(edit.isInsertion, true);
expect(edit.replacement, '!');
- var info = edit.info;
+ var info = edit.info!;
expect(info.description, NullabilityFixDescription.checkExpression);
var reasons = info.fixReasons;
expect(reasons, hasLength(1));
@@ -418,7 +420,7 @@
expect(edge.destinationNode,
same(explicitTypeNullability[findNode.typeAnnotation('int x')]));
expect(edge.isSatisfied, false);
- expect(edgeOrigin[edge].node, same(yUsage));
+ expect(edgeOrigin[edge]!.node, same(yUsage));
}
Future<void> test_fix_reason_node() async {
@@ -426,13 +428,13 @@
int x = null;
''');
var intAnnotation = findNode.typeAnnotation('int');
- var entries = changes.entries.toList();
+ var entries = changes!.entries.toList();
expect(entries, hasLength(1));
expect(entries.single.key, intAnnotation.end);
var edit = entries.single.value.single;
expect(edit.isInsertion, true);
expect(edit.replacement, '?');
- var info = edit.info;
+ var info = edit.info!;
expect(info.description, NullabilityFixDescription.makeTypeNullable('int'));
var reasons = info.fixReasons;
expect(reasons, hasLength(1));
@@ -445,8 +447,8 @@
await analyze(content, warnOnWeakCode: false);
var commentPos = content.indexOf('/*');
var questionDotPos = content.indexOf('?.');
- expect(changes.keys, unorderedEquals([commentPos, questionDotPos]));
- assertEdit(changes[questionDotPos].single,
+ expect(changes!.keys, unorderedEquals([commentPos, questionDotPos]));
+ assertEdit(changes![questionDotPos]!.single,
description: NullabilityFixDescription.removeNullAwareness,
fixReasons: isEmpty);
}
@@ -457,8 +459,8 @@
await analyze(content, warnOnWeakCode: false);
var commentPos = content.indexOf('/*');
var questionDotPos = content.indexOf('?.');
- expect(changes.keys, unorderedEquals([commentPos, questionDotPos]));
- assertEdit(changes[questionDotPos].single,
+ expect(changes!.keys, unorderedEquals([commentPos, questionDotPos]));
+ assertEdit(changes![questionDotPos]!.single,
description: NullabilityFixDescription.removeNullAwareness,
fixReasons: isEmpty);
}
@@ -474,18 +476,18 @@
var asExpression = xRef.parent as Expression;
expect(changes, hasLength(3));
// Change #1: drop the `(` before the cast
- var dropLeadingParen = changes[asExpression.offset - 1].single;
+ var dropLeadingParen = changes![asExpression.offset - 1]!.single;
expect(dropLeadingParen.isDeletion, true);
expect(dropLeadingParen.length, 1);
expect(dropLeadingParen.info, null);
// Change #2: drop the text ` as int`
- var dropAsInt = changes[xRef.end].single;
+ var dropAsInt = changes![xRef.end]!.single;
expect(dropAsInt.isDeletion, true);
expect(dropAsInt.length, 7);
- expect(dropAsInt.info.description, NullabilityFixDescription.removeAs);
- expect(dropAsInt.info.fixReasons, isEmpty);
+ expect(dropAsInt.info!.description, NullabilityFixDescription.removeAs);
+ expect(dropAsInt.info!.fixReasons, isEmpty);
// Change #3: drop the `)` after the cast
- var dropTrailingParen = changes[asExpression.end].single;
+ var dropTrailingParen = changes![asExpression.end]!.single;
expect(dropTrailingParen.isDeletion, true);
expect(dropTrailingParen.length, 1);
expect(dropTrailingParen.info, null);
@@ -499,10 +501,10 @@
''');
var intAnnotation = findNode.typeAnnotation('int');
expect(changes, isNotEmpty);
- for (var change in changes.values) {
+ for (var change in changes!.values) {
expect(change, isNotEmpty);
for (var edit in change) {
- var info = edit.info;
+ var info = edit.info!;
expect(info.description,
NullabilityFixDescription.addRequired(null, '_f', 'i'));
expect(info.fixReasons[FixReasonTarget.root],
@@ -577,10 +579,10 @@
f1(null, false);
}
''');
- var iNode = explicitTypeNullability[findNode.typeAnnotation('int i')];
- var jNode = explicitTypeNullability[findNode.typeAnnotation('int j')];
- var kNode = explicitTypeNullability[findNode.typeAnnotation('int k')];
- var lNode = explicitTypeNullability[findNode.typeAnnotation('int l')];
+ var iNode = explicitTypeNullability[findNode.typeAnnotation('int i')]!;
+ var jNode = explicitTypeNullability[findNode.typeAnnotation('int j')]!;
+ var kNode = explicitTypeNullability[findNode.typeAnnotation('int k')]!;
+ var lNode = explicitTypeNullability[findNode.typeAnnotation('int l')]!;
var iToJ = edges
.where((e) => e.sourceNode == iNode && e.destinationNode == jNode)
.single;
@@ -670,7 +672,7 @@
var matchingEdges = edges
.where((e) => e.sourceNode == xNode && e.destinationNode == returnNode)
.toList();
- var origin = edgeOrigin[matchingEdges.single];
+ var origin = edgeOrigin[matchingEdges.single]!;
expect(origin.source, source);
expect(origin.node, findNode.simple('x;'));
}
@@ -684,7 +686,7 @@
var matchingEdges = edges
.where((e) => e.sourceNode == xNode && e.destinationNode == returnNode)
.toList();
- var origin = edgeOrigin[matchingEdges.single];
+ var origin = edgeOrigin[matchingEdges.single]!;
expect(origin.kind, EdgeOriginKind.dynamicAssignment);
expect(origin.source, source);
expect(origin.node, findNode.simple('x;'));
@@ -711,8 +713,8 @@
await analyze('''
int x = null;
''');
- expect(always.isImmutable, true);
- expect(always.isNullable, true);
+ expect(always!.isImmutable, true);
+ expect(always!.isNullable, true);
var xNode = explicitTypeNullability[findNode.typeAnnotation('int')];
var edge = edges.where((e) => e.destinationNode == xNode).single;
var edgeSource = edge.sourceNode;
@@ -725,8 +727,8 @@
await analyze('''
bool f(int x) => x.isEven;
''');
- expect(never.isImmutable, true);
- expect(never.isNullable, false);
+ expect(never!.isImmutable, true);
+ expect(never!.isNullable, false);
var xNode = explicitTypeNullability[findNode.typeAnnotation('int')];
var edge = edges.where((e) => e.sourceNode == xNode).single;
expect(edge.destinationNode, never);
@@ -740,7 +742,8 @@
}
C f(bool b) => b ? C.named() : null;
''');
- var factoryReturnNode = implicitReturnType[findNode.constructor('C(')].node;
+ var factoryReturnNode =
+ implicitReturnType[findNode.constructor('C(')]!.node;
var fReturnNode = explicitTypeNullability[findNode.typeAnnotation('C f')];
expect(
edges.where((e) =>
@@ -753,9 +756,9 @@
await analyze('''
Object f(callback()) => callback();
''');
- var paramReturnNode =
- implicitReturnType[findNode.functionTypedFormalParameter('callback())')]
- .node;
+ var paramReturnNode = implicitReturnType[
+ findNode.functionTypedFormalParameter('callback())')]!
+ .node;
var fReturnNode =
explicitTypeNullability[findNode.typeAnnotation('Object')];
expect(
@@ -771,7 +774,7 @@
Object g() => f();
''');
var fReturnNode =
- implicitReturnType[findNode.functionDeclaration('f() =>')].node;
+ implicitReturnType[findNode.functionDeclaration('f() =>')]!.node;
var gReturnNode =
explicitTypeNullability[findNode.typeAnnotation('Object')];
expect(
@@ -790,7 +793,7 @@
var fReturnNode =
explicitTypeNullability[findNode.typeAnnotation('int Function')];
var functionExpressionReturnNode =
- implicitReturnType[findNode.functionExpression('() => g()')].node;
+ implicitReturnType[findNode.functionExpression('() => g()')]!.node;
var gReturnNode = explicitTypeNullability[findNode.typeAnnotation('int g')];
expect(
edges.where((e) =>
@@ -811,7 +814,7 @@
Object f(F callback) => callback();
''');
var typedefReturnNode =
- implicitReturnType[findNode.functionTypeAlias('F()')].node;
+ implicitReturnType[findNode.functionTypeAlias('F()')]!.node;
var fReturnNode =
explicitTypeNullability[findNode.typeAnnotation('Object')];
expect(
@@ -826,7 +829,7 @@
Object f(Function() callback) => callback();
''');
var callbackReturnNode =
- implicitReturnType[findNode.genericFunctionType('Function()')].node;
+ implicitReturnType[findNode.genericFunctionType('Function()')]!.node;
var fReturnNode =
explicitTypeNullability[findNode.typeAnnotation('Object')];
expect(
@@ -848,7 +851,7 @@
var baseReturnNode =
explicitTypeNullability[findNode.typeAnnotation('int')];
var derivedReturnNode =
- implicitReturnType[findNode.methodDeclaration('f /*derived*/')].node;
+ implicitReturnType[findNode.methodDeclaration('f /*derived*/')]!.node;
expect(
edges.where((e) =>
e.sourceNode == derivedReturnNode &&
@@ -865,7 +868,7 @@
}
''');
var oNode = explicitTypeNullability[findNode.typeAnnotation('Object')];
- var eNode = implicitType[findNode.simple('e)')].node;
+ var eNode = implicitType[findNode.simple('e)')]!.node;
expect(
edges.where((e) => e.sourceNode == eNode && e.destinationNode == oNode),
hasLength(1));
@@ -880,7 +883,7 @@
}
''');
var oNode = explicitTypeNullability[findNode.typeAnnotation('Object')];
- var stNode = implicitType[findNode.simple('st)')].node;
+ var stNode = implicitType[findNode.simple('st)')]!.node;
expect(
edges
.where((e) => e.sourceNode == stNode && e.destinationNode == oNode),
@@ -898,7 +901,7 @@
''');
var xNode = implicitType[(findNode.forStatement('for').forLoopParts
as ForEachPartsWithDeclaration)
- .loopVariable]
+ .loopVariable]!
.node;
var yNode = explicitTypeNullability[findNode.typeAnnotation('int y')];
expect(
@@ -918,7 +921,7 @@
var baseParamNode =
explicitTypeNullability[findNode.typeAnnotation('int i')];
var derivedParamNode =
- implicitType[findNode.simpleParameter('i); /*derived*/')].node;
+ implicitType[findNode.simpleParameter('i); /*derived*/')]!.node;
expect(
edges.where((e) =>
e.sourceNode == baseParamNode &&
@@ -938,8 +941,8 @@
var baseParamParamNode =
explicitTypeNullability[findNode.typeAnnotation('int i')];
var derivedParamParamNode =
- implicitType[findNode.simpleParameter('callback)')]
- .namedParameter('i')
+ implicitType[findNode.simpleParameter('callback)')]!
+ .namedParameter('i')!
.node;
expect(
edges.where((e) =>
@@ -960,8 +963,8 @@
var baseParamParamNode =
explicitTypeNullability[findNode.typeAnnotation('int i')];
var derivedParamParamNode =
- implicitType[findNode.simpleParameter('callback)')]
- .positionalParameter(0)
+ implicitType[findNode.simpleParameter('callback)')]!
+ .positionalParameter(0)!
.node;
expect(
edges.where((e) =>
@@ -982,7 +985,7 @@
var baseParamReturnNode =
explicitTypeNullability[findNode.typeAnnotation('int callback')];
var derivedParamReturnNode =
- implicitType[findNode.simpleParameter('callback)')].returnType.node;
+ implicitType[findNode.simpleParameter('callback)')]!.returnType!.node;
expect(
edges.where((e) =>
e.sourceNode == baseParamReturnNode &&
@@ -1002,8 +1005,8 @@
var baseParamArgNode =
explicitTypeNullability[findNode.typeAnnotation('int>')];
var derivedParamArgNode =
- implicitType[findNode.simpleParameter('x); /*derived*/')]
- .typeArgument(0)
+ implicitType[findNode.simpleParameter('x); /*derived*/')]!
+ .typeArgument(0)!
.node;
expect(
edges.where((e) =>
@@ -1019,7 +1022,7 @@
}
''');
var iNode = explicitTypeNullability[findNode.typeAnnotation('int')];
- var jNode = implicitType[findNode.variableDeclarationList('j')].node;
+ var jNode = implicitType[findNode.variableDeclarationList('j')]!.node;
expect(
edges.where((e) => e.sourceNode == iNode && e.destinationNode == jNode),
hasLength(1));
@@ -1031,7 +1034,9 @@
List<int> f() => g(null);
''');
var implicitInvocationTypeArgumentNode =
- implicitTypeArguments[findNode.methodInvocation('g(null)')].single.node;
+ implicitTypeArguments[findNode.methodInvocation('g(null)')]!
+ .single
+ .node;
var returnElementNode =
explicitTypeNullability[findNode.typeAnnotation('int')];
expect(edges.where((e) {
@@ -1056,7 +1061,7 @@
List<int> f(C c) => c.g(null);
''');
var implicitInvocationTypeArgumentNode =
- implicitTypeArguments[findNode.methodInvocation('c.g(null)')]
+ implicitTypeArguments[findNode.methodInvocation('c.g(null)')]!
.single
.node;
var returnElementNode =
@@ -1083,7 +1088,9 @@
C<int> f() => C(null);
''');
var implicitInvocationTypeArgumentNode =
- implicitTypeArguments[findNode.instanceCreation('C(null)')].single.node;
+ implicitTypeArguments[findNode.instanceCreation('C(null)')]!
+ .single
+ .node;
var returnElementNode =
explicitTypeNullability[findNode.typeAnnotation('int')];
expect(edges.where((e) {
@@ -1104,7 +1111,7 @@
List<int> f() => [null];
''');
var implicitListLiteralElementNode =
- implicitTypeArguments[findNode.listLiteral('[null]')].single.node;
+ implicitTypeArguments[findNode.listLiteral('[null]')]!.single.node;
var returnElementNode =
explicitTypeNullability[findNode.typeAnnotation('int')];
expect(
@@ -1124,7 +1131,7 @@
Map<int, String> f() => {1: null};
''');
var implicitMapLiteralTypeArguments =
- implicitTypeArguments[findNode.setOrMapLiteral('{1: null}')];
+ implicitTypeArguments[findNode.setOrMapLiteral('{1: null}')]!;
expect(implicitMapLiteralTypeArguments, hasLength(2));
var implicitMapLiteralKeyNode = implicitMapLiteralTypeArguments[0].node;
var implicitMapLiteralValueNode = implicitMapLiteralTypeArguments[1].node;
@@ -1158,7 +1165,7 @@
Set<int> f() => {null};
''');
var implicitSetLiteralElementNode =
- implicitTypeArguments[findNode.setOrMapLiteral('{null}')].single.node;
+ implicitTypeArguments[findNode.setOrMapLiteral('{null}')]!.single.node;
var returnElementNode =
explicitTypeNullability[findNode.typeAnnotation('int')];
expect(
@@ -1178,7 +1185,7 @@
List<Object> f(List l) => l;
''');
var implicitListElementType =
- implicitTypeArguments[findNode.typeAnnotation('List l')].single.node;
+ implicitTypeArguments[findNode.typeAnnotation('List l')]!.single.node;
var implicitReturnElementType =
explicitTypeNullability[findNode.typeAnnotation('Object')];
expect(
@@ -1206,12 +1213,12 @@
explicitTypeNullability[findNode.typeAnnotation('T t')]);
}
- bool _isPointedToByAlways(NullabilityNodeInfo node) {
+ bool _isPointedToByAlways(NullabilityNodeInfo? node) {
return edges
.any((e) => e.sourceNode == always && e.destinationNode == node);
}
- bool _pointsToNeverHard(NullabilityNodeInfo node) {
+ bool _pointsToNeverHard(NullabilityNodeInfo? node) {
return edges.any(
(e) => e.sourceNode == node && e.destinationNode == never && e.isHard);
}
diff --git a/pkg/nnbd_migration/test/migration_cli_test.dart b/pkg/nnbd_migration/test/migration_cli_test.dart
index fb66e06..e4df376 100644
--- a/pkg/nnbd_migration/test/migration_cli_test.dart
+++ b/pkg/nnbd_migration/test/migration_cli_test.dart
@@ -17,7 +17,6 @@
import 'package:args/args.dart';
import 'package:cli_util/cli_logging.dart';
import 'package:http/http.dart' as http;
-import 'package:meta/meta.dart';
import 'package:nnbd_migration/instrumentation.dart';
import 'package:nnbd_migration/migration_cli.dart';
import 'package:nnbd_migration/src/front_end/dartfix_listener.dart';
@@ -47,7 +46,7 @@
class _ExceptionGeneratingInstrumentationListener
extends InstrumentationListener {
_ExceptionGeneratingInstrumentationListener(
- {MigrationSummary migrationSummary})
+ {MigrationSummary? migrationSummary})
: super(migrationSummary: migrationSummary);
@override
@@ -66,14 +65,14 @@
DartFixListener listener,
ResourceProvider resourceProvider,
LineInfo Function(String) getLineInfo,
- Object bindAddress,
+ Object? bindAddress,
Logger logger,
{List<String> included = const <String>[],
- int preferredPort,
- String summaryPath,
- @required String sdkPath})
+ int? preferredPort,
+ String? summaryPath,
+ required String sdkPath})
: super(listener, resourceProvider, getLineInfo, bindAddress, logger,
- (String path) => true,
+ (String? path) => true,
included: included,
preferredPort: preferredPort,
summaryPath: summaryPath,
@@ -81,7 +80,7 @@
@override
InstrumentationListener createInstrumentationListener(
- {MigrationSummary migrationSummary}) =>
+ {MigrationSummary? migrationSummary}) =>
_ExceptionGeneratingInstrumentationListener(
migrationSummary: migrationSummary);
}
@@ -91,7 +90,7 @@
/// If non-null, callback function that will be invoked by the `applyHook`
/// override.
- void Function() _onApplyHook;
+ void Function()? _onApplyHook;
_MigrationCli(this._test)
: super(
@@ -102,8 +101,8 @@
resourceProvider: _test.resourceProvider,
environmentVariables: _test.environmentVariables);
- _MigrationCliRunner decodeCommandLineArgs(ArgResults argResults,
- {bool isVerbose}) {
+ _MigrationCliRunner? decodeCommandLineArgs(ArgResults argResults,
+ {bool? isVerbose}) {
var runner = super.decodeCommandLineArgs(argResults, isVerbose: isVerbose);
if (runner == null) return null;
return _MigrationCliRunner(this, runner.options);
@@ -111,7 +110,7 @@
}
class _MigrationCliRunner extends MigrationCliRunner {
- Future<void> Function() _runWhilePreviewServerActive;
+ Future<void> Function()? _runWhilePreviewServerActive;
_MigrationCliRunner(_MigrationCli cli, CommandLineOptions options)
: super(cli, options);
@@ -125,7 +124,7 @@
}
@override
- Object computeBindAddress() {
+ Object? computeBindAddress() {
var address = super.computeBindAddress();
if (Platform.environment.containsKey('FORCE_IPV6') &&
address == InternetAddress.loopbackIPv4) {
@@ -144,11 +143,11 @@
DartFixListener listener,
ResourceProvider resourceProvider,
LineInfo Function(String path) getLineInfo,
- Object bindAddress,
+ Object? bindAddress,
{List<String> included = const <String>[],
- int preferredPort,
- String summaryPath,
- @required String sdkPath}) {
+ int? preferredPort,
+ String? summaryPath,
+ required String sdkPath}) {
if (cli._test.injectArtificialException) {
return _ExceptionGeneratingNonNullableFix(
listener, resourceProvider, getLineInfo, bindAddress, logger,
@@ -172,7 +171,9 @@
fail('Preview server not expected to have been started');
}
sigIntSignalled = Completer();
- _runWhilePreviewServerActive.call().then((_) => sigIntSignalled.complete());
+ _runWhilePreviewServerActive!
+ .call()
+ .then((_) => sigIntSignalled.complete());
_runWhilePreviewServerActive = null;
}
@@ -206,9 +207,9 @@
/// If non-null, this is injected as the return value for
/// [_MigrationCliRunner.computePathsToProcess].
- Set<String> overridePathsToProcess;
+ Set<String>? overridePathsToProcess;
- bool Function(String) overrideShouldBeMigrated;
+ bool Function(String)? overrideShouldBeMigrated;
set logger(TestLogger logger);
@@ -217,7 +218,7 @@
mixin _MigrationCliTestMethods on _MigrationCliTestBase {
@override
- /*late*/ TestLogger logger;
+ late TestLogger logger;
final hasVerboseHelpMessage = contains('for verbose help output');
@@ -245,7 +246,7 @@
Future<String> assertErrorExit(
MigrationCliRunner cliRunner, FutureOr<void> Function() callback,
- {@required bool withUsage, dynamic expectedExitCode}) async {
+ {required bool withUsage, dynamic expectedExitCode}) async {
expectedExitCode ??= isNot(0);
try {
await callback();
@@ -289,7 +290,7 @@
CommandLineOptions assertParseArgsSuccess(List<String> args) {
var cliRunner = _createCli()
- .decodeCommandLineArgs(MigrationCli.createParser().parse(args));
+ .decodeCommandLineArgs(MigrationCli.createParser().parse(args))!;
assertNormalExit(cliRunner);
var options = cliRunner.options;
expect(options, isNotNull);
@@ -301,7 +302,7 @@
assertHttpSuccess(response);
}
- void assertProjectContents(String projectDir, Map<String, String> expected) {
+ void assertProjectContents(String projectDir, Map<String, String?> expected) {
for (var entry in expected.entries) {
var relativePathPosix = entry.key;
assert(!path.posix.isAbsolute(relativePathPosix));
@@ -313,12 +314,12 @@
}
Future<String> assertRunFailure(List<String> args,
- {MigrationCli cli,
+ {MigrationCli? cli,
bool withUsage = false,
dynamic expectedExitCode}) async {
expectedExitCode ??= isNot(0);
cli ??= _createCli();
- MigrationCliRunner cliRunner;
+ MigrationCliRunner? cliRunner;
try {
cliRunner =
cli.decodeCommandLineArgs(MigrationCli.createParser().parse(args));
@@ -327,11 +328,11 @@
expect(e.exitCode, expectedExitCode);
return assertStderr(withUsage: withUsage);
}
- return await assertErrorExit(cliRunner, () => cliRunner.run(),
+ return await assertErrorExit(cliRunner!, () => cliRunner!.run(),
withUsage: withUsage, expectedExitCode: expectedExitCode);
}
- String assertStderr({@required bool withUsage}) {
+ String assertStderr({required bool withUsage}) {
var stderrText = logger.stderrBuffer.toString();
expect(stderrText, withUsage ? hasUsageText : isNot(hasUsageText));
expect(stderrText,
@@ -352,34 +353,34 @@
return response;
}
- String createProjectDir(Map<String, String> contents,
+ String createProjectDir(Map<String, String?> contents,
{String posixPath = '/test_project'}) {
for (var entry in contents.entries) {
var relativePathPosix = entry.key;
assert(!path.posix.isAbsolute(relativePathPosix));
var filePathPosix = path.posix.join(posixPath, relativePathPosix);
resourceProvider.newFile(
- resourceProvider.convertPath(filePathPosix), entry.value);
+ resourceProvider.convertPath(filePathPosix), entry.value!);
}
return resourceProvider.convertPath(posixPath);
}
- Future<String> getSourceFromServer(Uri uri, String path) async {
+ Future<String?> getSourceFromServer(Uri uri, String path) async {
http.Response response = await tryGetSourceFromServer(uri, path);
assertHttpSuccess(response);
- return jsonDecode(response.body)['sourceCode'] as String;
+ return jsonDecode(response.body)['sourceCode'] as String?;
}
/// Performs an HTTP get, verifying that the response received (if any) is
/// reasonable.
- Future<http.Response> httpGet(Uri url, {Map<String, String> headers}) {
+ Future<http.Response> httpGet(Uri url, {Map<String, String>? headers}) {
return checkHttpResponse(http.get(url, headers: headers));
}
/// Performs an HTTP post, verifying that the response received (if any) is
/// reasonable.
Future<http.Response> httpPost(Uri url,
- {Map<String, String> headers, dynamic body, Encoding encoding}) {
+ {Map<String, String>? headers, dynamic body, Encoding? encoding}) {
return checkHttpResponse(
http.post(url, headers: headers, body: body, encoding: encoding));
}
@@ -388,8 +389,8 @@
resourceProvider.convertPath('/.pub-cache/$path');
Future<void> runWithPreviewServer(_MigrationCli cli, List<String> args,
- Future<void> Function(String) callback) async {
- String url;
+ Future<void> Function(String?) callback) async {
+ String? url;
var cliRunner = cli.decodeCommandLineArgs(_parseArgs(args));
if (cliRunner != null) {
await cliRunner.runWithPreviewServer(() async {
@@ -399,7 +400,7 @@
await callback(url);
});
// Server should be stopped now
- expect(httpGet(Uri.parse(url)), throwsA(anything));
+ expect(httpGet(Uri.parse(url!)), throwsA(anything));
assertNormalExit(cliRunner);
}
}
@@ -409,12 +410,12 @@
environmentVariables.clear();
}
- Map<String, String> simpleProject(
+ Map<String, String?> simpleProject(
{bool migrated = false,
- String sourceText,
- String pubspecText,
- String packageConfigText,
- String analysisOptionsText}) {
+ String? sourceText,
+ String? pubspecText,
+ String? packageConfigText,
+ String? analysisOptionsText}) {
return {
'pubspec.yaml': pubspecText ??
'''
@@ -582,7 +583,7 @@
}
test_lifecycle_already_migrated_file() async {
- Map<String, String> createProject({bool migrated = false}) {
+ Map<String, String?> createProject({bool migrated = false}) {
var projectContents = simpleProject(sourceText: '''
${migrated ? '' : '// @dart = 2.6'}
import 'already_migrated.dart';
@@ -597,7 +598,7 @@
var projectContents = createProject();
var projectDir = createProjectDir(projectContents);
var cliRunner = _createCli(nullSafePackages: ['test'])
- .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+ .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!;
await cliRunner.run();
assertNormalExit(cliRunner);
// Check that a summary was printed
@@ -619,7 +620,7 @@
var projectDir = createProjectDir(projectContents);
var cli = _createCli();
var cliRunner =
- cli.decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+ cli.decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!;
bool applyHookCalled = false;
cli._onApplyHook = () {
expect(applyHookCalled, false);
@@ -656,7 +657,7 @@
var projectDir = createProjectDir(projectContents);
var cliRunner = _createCli()
- .decodeCommandLineArgs(_parseArgs(['--no-web-preview', projectDir]));
+ .decodeCommandLineArgs(_parseArgs(['--no-web-preview', projectDir]))!;
await cliRunner.run();
assertNormalExit(cliRunner);
expect(cliRunner.hasMultipleAnalysisContext, true);
@@ -669,7 +670,7 @@
var projectContents = simpleProject();
var projectDir = createProjectDir(projectContents);
var cliRunner = _createCli()
- .decodeCommandLineArgs(_parseArgs(['--no-web-preview', projectDir]));
+ .decodeCommandLineArgs(_parseArgs(['--no-web-preview', projectDir]))!;
await cliRunner.run();
assertNormalExit(cliRunner);
expect(cliRunner.hasMultipleAnalysisContext, false);
@@ -713,7 +714,7 @@
contains('Attempting to perform\nmigration anyway due to the use'
' of --ignore-exceptions.'));
expect(output, contains('re-run without --ignore-exceptions'));
- await assertPreviewServerResponsive(url);
+ await assertPreviewServerResponsive(url!);
await _tellPreviewToApplyChanges(url);
assertProjectContents(
projectDir, simpleProject(migrated: true, sourceText: '''
@@ -784,7 +785,7 @@
output,
contains('Continuing with migration suggestions due to the use of '
'--ignore-errors.'));
- await assertPreviewServerResponsive(url);
+ await assertPreviewServerResponsive(url!);
});
}
@@ -833,7 +834,7 @@
}
test_lifecycle_import_lib_from_test() async {
- Map<String, String> makeProject({bool migrated = false}) {
+ Map<String, String?> makeProject({bool migrated = false}) {
return simpleProject(migrated: migrated)
..['test/foo.dart'] = '''
import '../lib/test.dart';
@@ -844,7 +845,7 @@
var projectDir = createProjectDir(projectContents);
var cli = _createCli();
var cliRunner =
- cli.decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+ cli.decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!;
bool applyHookCalled = false;
cli._onApplyHook = () {
expect(applyHookCalled, false);
@@ -894,7 +895,7 @@
var projectContents = simpleProject();
var projectDir = createProjectDir(projectContents);
var cliRunner = _createCli()
- .decodeCommandLineArgs(_parseArgs(['--no-web-preview', projectDir]));
+ .decodeCommandLineArgs(_parseArgs(['--no-web-preview', projectDir]))!;
await cliRunner.run();
assertNormalExit(cliRunner);
// Check that a summary was printed
@@ -912,7 +913,7 @@
}
test_lifecycle_override_paths() async {
- Map<String, String> makeProject({bool migrated = false}) {
+ Map<String, String?> makeProject({bool migrated = false}) {
var projectContents = simpleProject(migrated: migrated);
projectContents['lib/test.dart'] = '''
import 'skip.dart';
@@ -946,7 +947,7 @@
'--apply-changes',
'--skip-import-check',
projectDir
- ]));
+ ]))!;
await cliRunner.run();
assertNormalExit(cliRunner);
// Check that a summary was printed
@@ -972,7 +973,7 @@
expect(
logger.stdoutBuffer.toString(), contains('No analysis issues found'));
expect(url, startsWith('http://$localhostAddressText:'));
- await assertPreviewServerResponsive(url);
+ await assertPreviewServerResponsive(url!);
});
// No changes should have been made.
assertProjectContents(projectDir, projectContents);
@@ -985,7 +986,7 @@
await runWithPreviewServer(cli, [projectDir], (url) async {
expect(
logger.stdoutBuffer.toString(), contains('No analysis issues found'));
- await assertPreviewServerResponsive(url);
+ await assertPreviewServerResponsive(url!);
var uri = Uri.parse(url);
var authToken = uri.queryParameters['authToken'];
var response = await httpPost(
@@ -1021,7 +1022,7 @@
await runWithPreviewServer(cli, [projectDir], (url) async {
expect(
logger.stdoutBuffer.toString(), contains('No analysis issues found'));
- await assertPreviewServerResponsive(url);
+ await assertPreviewServerResponsive(url!);
await _tellPreviewToApplyChanges(url);
expect(applyHookCalled, true);
});
@@ -1046,7 +1047,7 @@
await runWithPreviewServer(cli, [projectDir], (url) async {
expect(
logger.stdoutBuffer.toString(), contains('No analysis issues found'));
- await assertPreviewServerResponsive(url);
+ await assertPreviewServerResponsive(url!);
await _tellPreviewToApplyChanges(url);
expect(applyHookCalled, true);
});
@@ -1056,7 +1057,7 @@
var projectDir = createProjectDir(simpleProject());
var cli = _createCli();
await runWithPreviewServer(cli, [projectDir], (url) async {
- var uri = Uri.parse(url);
+ var uri = Uri.parse(url!);
await assertPreviewServerResponsive(
uri.replace(path: uri.path + '/').toString());
});
@@ -1070,7 +1071,7 @@
await runWithPreviewServer(cli, [projectDir], (url) async {
expect(
logger.stdoutBuffer.toString(), contains('No analysis issues found'));
- await assertPreviewServerResponsive(url);
+ await assertPreviewServerResponsive(url!);
final uri = Uri.parse(url);
final authToken = uri.queryParameters['authToken'];
final fileResponse = await httpGet(
@@ -1082,10 +1083,10 @@
queryParameters: {'inline': 'true', 'authToken': authToken}),
headers: {'Content-Type': 'application/json; charset=UTF-8'});
final fileJson = FileDetails.fromJson(jsonDecode(fileResponse.body));
- final navigation = fileJson.navigationContent;
+ final navigation = fileJson.navigationContent!;
final aLink = RegExp(r'<a href="([^"]+)" class="nav-link">');
for (final match in aLink.allMatches(navigation)) {
- var href = match.group(1);
+ var href = match.group(1)!;
final contentsResponse = await httpGet(
uri.replace(
path: Uri.parse(href).path,
@@ -1103,7 +1104,7 @@
await runWithPreviewServer(cli, [projectDir], (url) async {
expect(
logger.stdoutBuffer.toString(), contains('No analysis issues found'));
- await assertPreviewServerResponsive(url);
+ await assertPreviewServerResponsive(url!);
var uri = Uri.parse(url);
var authToken = uri.queryParameters['authToken'];
var treeResponse = await httpGet(
@@ -1115,11 +1116,11 @@
for (final root in navRoots) {
var navTree =
NavigationTreeNode.fromJson(root) as NavigationTreeDirectoryNode;
- for (final file in navTree.subtree) {
+ for (final file in navTree.subtree!) {
if (file is NavigationTreeFileNode) {
final contentsResponse = await httpGet(
uri
- .resolve(file.href)
+ .resolve(file.href!)
.replace(queryParameters: {'authToken': authToken}),
headers: {'Content-Type': 'application/json; charset=UTF-8'});
assertHttpSuccess(contentsResponse);
@@ -1136,7 +1137,7 @@
..decodeCommandLineArgs(_parseArgs(['--preview-hostname=any']));
await runWithPreviewServer(cli, [projectDir], (url) async {
expect(url, isNot(contains('localhost')));
- await assertPreviewServerResponsive(url);
+ await assertPreviewServerResponsive(url!);
});
// No changes should have been made.
assertProjectContents(projectDir, projectContents);
@@ -1149,7 +1150,7 @@
await runWithPreviewServer(cli, [projectDir], (url) async {
expect(
logger.stdoutBuffer.toString(), contains('No analysis issues found'));
- await assertPreviewServerResponsive(url);
+ await assertPreviewServerResponsive(url!);
var uri = Uri.parse(url);
var authToken = uri.queryParameters['authToken'];
var regionResponse = await httpGet(
@@ -1165,7 +1166,7 @@
}),
headers: {'Content-Type': 'application/json; charset=UTF-8'});
var regionJson = EditDetails.fromJson(jsonDecode(regionResponse.body));
- final displayPath = regionJson.displayPath;
+ final displayPath = regionJson.displayPath!;
final uriPath = regionJson.uriPath;
// uriPath should be a working URI
final contentsResponse = await httpGet(
@@ -1190,7 +1191,7 @@
await runWithPreviewServer(cli, [projectDir], (url) async {
expect(
logger.stdoutBuffer.toString(), contains('No analysis issues found'));
- await assertPreviewServerResponsive(url);
+ await assertPreviewServerResponsive(url!);
final uri = Uri.parse(url);
final authToken = uri.queryParameters['authToken'];
final fileResponse = await httpGet(
@@ -1202,10 +1203,10 @@
queryParameters: {'inline': 'true', 'authToken': authToken}),
headers: {'Content-Type': 'application/json; charset=UTF-8'});
final fileJson = FileDetails.fromJson(jsonDecode(fileResponse.body));
- final regions = fileJson.regions;
+ final regions = fileJson.regions!;
final regionsPathRegex = RegExp(r'<table data-path="([^"]+)">');
expect(regionsPathRegex.hasMatch(regions), true);
- final regionsPath = regionsPathRegex.matchAsPrefix(regions).group(1);
+ final regionsPath = regionsPathRegex.matchAsPrefix(regions)!.group(1)!;
final contentsResponse = await httpGet(
uri.replace(
path: Uri.parse(regionsPath).path,
@@ -1221,7 +1222,7 @@
var projectDir = createProjectDir(projectContents);
var cli = _createCli();
await runWithPreviewServer(cli, [projectDir], (url) async {
- await assertPreviewServerResponsive(url);
+ await assertPreviewServerResponsive(url!);
var uri = Uri.parse(url);
var testPath =
resourceProvider.pathContext.join(projectDir, 'lib', 'test.dart');
@@ -1243,7 +1244,7 @@
var projectDir = createProjectDir(projectContents);
var cli = _createCli();
await runWithPreviewServer(cli, [projectDir], (url) async {
- await assertPreviewServerResponsive(url);
+ await assertPreviewServerResponsive(url!);
var uri = Uri.parse(url);
var test2Path =
resourceProvider.pathContext.join(projectDir, 'lib', 'test2.dart');
@@ -1269,7 +1270,7 @@
var summaryPath = resourceProvider.convertPath('/summary.json');
await runWithPreviewServer(cli, ['--summary', summaryPath, projectDir],
(url) async {
- await assertPreviewServerResponsive(url);
+ await assertPreviewServerResponsive(url!);
// lib/test.dart should be readable from the server and appear in the
// summary
var uri = Uri.parse(url);
@@ -1304,7 +1305,7 @@
var cli = _createCli();
await runWithPreviewServer(cli, ['--ignore-errors', projectDir],
(url) async {
- await assertPreviewServerResponsive(url);
+ await assertPreviewServerResponsive(url!);
var uri = Uri.parse(url);
var testPath =
resourceProvider.pathContext.join(projectDir, 'lib', 'test.dart');
@@ -1329,7 +1330,7 @@
var projectDir = createProjectDir(projectContents);
var cli = _createCli();
await runWithPreviewServer(cli, [projectDir], (url) async {
- await assertPreviewServerResponsive(url);
+ await assertPreviewServerResponsive(url!);
var uri = Uri.parse(url);
var testPath =
resourceProvider.pathContext.join(projectDir, 'lib', 'test.dart');
@@ -1365,7 +1366,7 @@
createProjectDir(projectContents, posixPath: '/other_project_dir');
var cli = _createCli();
await runWithPreviewServer(cli, [mainProjectDir], (url) async {
- await assertPreviewServerResponsive(url);
+ await assertPreviewServerResponsive(url!);
var uri = Uri.parse(url);
Future<http.Response> tryGetSourceFromProject(String projectDir) =>
@@ -1396,7 +1397,7 @@
await runWithPreviewServer(cli, [projectDir], (url) async {
expect(
logger.stdoutBuffer.toString(), contains('No analysis issues found'));
- await assertPreviewServerResponsive(url);
+ await assertPreviewServerResponsive(url!);
var uri = Uri.parse(url);
var authToken = uri.queryParameters['authToken'];
var regionResponse = await httpGet(
@@ -1431,7 +1432,7 @@
await runWithPreviewServer(cli, [projectDir], (url) async {
expect(
logger.stdoutBuffer.toString(), contains('No analysis issues found'));
- await assertPreviewServerResponsive(url);
+ await assertPreviewServerResponsive(url!);
var uri = Uri.parse(url);
var authToken = uri.queryParameters['authToken'];
var regionUri = uri.replace(
@@ -1447,8 +1448,8 @@
var regionResponse = await httpGet(regionUri,
headers: {'Content-Type': 'application/json; charset=UTF-8'});
var regionJson = EditDetails.fromJson(jsonDecode(regionResponse.body));
- final traceEntry = regionJson.traces[0].entries[0];
- final uriPath = traceEntry.link.href;
+ final traceEntry = regionJson.traces![0].entries[0];
+ final uriPath = traceEntry.link!.href!;
// uriPath should be a working URI
final contentsResponse = await httpGet(
regionUri
@@ -1499,7 +1500,7 @@
var cli = _createCli();
await runWithPreviewServer(cli, ['--skip-import-check', projectDir],
(url) async {
- await assertPreviewServerResponsive(url);
+ await assertPreviewServerResponsive(url!);
var output = logger.stdoutBuffer.toString();
expect(output, contains('Warning: package has unmigrated dependencies'));
// Output should not mention the particular unmigrated dependencies.
@@ -1514,8 +1515,8 @@
var projectContents = simpleProject();
var projectDir = createProjectDir(projectContents);
var summaryPath = resourceProvider.convertPath('/summary.json');
- var cliRunner = _createCli().decodeCommandLineArgs(
- _parseArgs(['--no-web-preview', '--summary', summaryPath, projectDir]));
+ var cliRunner = _createCli().decodeCommandLineArgs(_parseArgs(
+ ['--no-web-preview', '--summary', summaryPath, projectDir]))!;
await cliRunner.run();
var summaryData =
jsonDecode(resourceProvider.getFile(summaryPath).readAsStringSync());
@@ -1528,8 +1529,8 @@
var projectContents = simpleProject(sourceText: 'int/*?*/ x;');
var projectDir = createProjectDir(projectContents);
var summaryPath = resourceProvider.convertPath('/summary.json');
- var cliRunner = _createCli().decodeCommandLineArgs(
- _parseArgs(['--no-web-preview', '--summary', summaryPath, projectDir]));
+ var cliRunner = _createCli().decodeCommandLineArgs(_parseArgs(
+ ['--no-web-preview', '--summary', summaryPath, projectDir]))!;
await cliRunner.run();
assertNormalExit(cliRunner);
var summaryData =
@@ -1546,7 +1547,7 @@
var summaryPath = resourceProvider.convertPath('/summary.json');
await runWithPreviewServer(cli, ['--summary', summaryPath, projectDir],
(url) async {
- await assertPreviewServerResponsive(url);
+ await assertPreviewServerResponsive(url!);
var summaryData =
jsonDecode(resourceProvider.getFile(summaryPath).readAsStringSync());
var separator = resourceProvider.pathContext.separator;
@@ -1659,7 +1660,7 @@
test_option_sdk_default() {
var cli = MigrationCli(binaryName: 'nnbd_migration');
- var cliRunner = cli.decodeCommandLineArgs(_parseArgs([]));
+ var cliRunner = cli.decodeCommandLineArgs(_parseArgs([]))!;
expect(Directory(path.join(cliRunner.options.sdkPath, 'bin')).existsSync(),
isTrue);
}
@@ -1695,7 +1696,7 @@
_expectErrorIndicatingCodeIsAlreadyOptedIn();
} else {
var cliRunner = _createCli()
- .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+ .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!;
await cliRunner.run();
// The Dart source code should still be migrated.
assertProjectContents(
@@ -1728,7 +1729,7 @@
_expectErrorIndicatingCodeIsAlreadyOptedIn();
} else {
var cliRunner = _createCli()
- .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+ .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!;
await cliRunner.run();
// The Dart source code should still be migrated.
assertProjectContents(projectDir,
@@ -1754,7 +1755,7 @@
_expectErrorIndicatingCodeIsAlreadyOptedIn();
} else {
var cliRunner = _createCli()
- .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+ .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!;
await cliRunner.run();
// The Dart source code should still be migrated.
assertProjectContents(projectDir,
@@ -1785,7 +1786,7 @@
_expectErrorIndicatingCodeIsAlreadyOptedIn();
} else {
var cliRunner = _createCli()
- .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+ .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!;
await cliRunner.run();
// The Dart source code should still be migrated.
assertProjectContents(projectDir,
@@ -1806,7 +1807,7 @@
''');
var projectDir = createProjectDir(projectContents);
var cliRunner = _createCli()
- .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+ .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!;
await cliRunner.run();
expect(
logger.stdoutBuffer.toString(), contains('Please run `dart pub get`'));
@@ -1836,7 +1837,7 @@
''');
var projectDir = createProjectDir(projectContents);
var cliRunner = _createCli()
- .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+ .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!;
await cliRunner.run();
expect(
logger.stdoutBuffer.toString(), contains('Please run `dart pub get`'));
@@ -1866,7 +1867,7 @@
''');
var projectDir = createProjectDir(projectContents);
var cliRunner = _createCli()
- .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+ .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!;
await cliRunner.run();
expect(
logger.stdoutBuffer.toString(), contains('Please run `dart pub get`'));
@@ -1892,7 +1893,7 @@
var projectContents = simpleProject()..remove('pubspec.yaml');
var projectDir = createProjectDir(projectContents);
var cliRunner = _createCli()
- .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+ .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!;
await cliRunner.run();
// The Dart source code should still be migrated.
assertProjectContents(
@@ -1912,7 +1913,7 @@
''');
var projectDir = createProjectDir(projectContents);
var cliRunner = _createCli()
- .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+ .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!;
await cliRunner.run();
// The Dart source code should still be migrated.
assertProjectContents(
@@ -1932,7 +1933,7 @@
var projectContents = simpleProject(pubspecText: pubspecText);
var projectDir = createProjectDir(projectContents);
var cliRunner = _createCli()
- .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+ .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!;
await cliRunner.run();
// The Dart source code should still be migrated.
assertProjectContents(
@@ -1948,7 +1949,7 @@
var projectContents = simpleProject(pubspecText: pubspecText);
var projectDir = createProjectDir(projectContents);
var cliRunner = _createCli()
- .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+ .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!;
await cliRunner.run();
// The Dart source code should still be migrated.
assertProjectContents(
@@ -1969,7 +1970,7 @@
var projectContents = simpleProject(pubspecText: pubspecText);
var projectDir = createProjectDir(projectContents);
var cliRunner = _createCli()
- .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+ .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!;
await cliRunner.run();
// The Dart source code should still be migrated.
assertProjectContents(
@@ -1990,7 +1991,7 @@
var projectContents = simpleProject(pubspecText: pubspecText);
var projectDir = createProjectDir(projectContents);
var cliRunner = _createCli()
- .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+ .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!;
await cliRunner.run();
// The Dart source code should still be migrated.
assertProjectContents(
@@ -2011,7 +2012,7 @@
var projectContents = simpleProject(pubspecText: pubspecText);
var projectDir = createProjectDir(projectContents);
var cliRunner = _createCli()
- .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+ .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!;
await cliRunner.run();
// The Dart source code should still be migrated.
assertProjectContents(
@@ -2033,7 +2034,7 @@
''');
var projectDir = createProjectDir(projectContents);
var cliRunner = _createCli()
- .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+ .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!;
await cliRunner.run();
// The Dart source code should still be migrated.
assertProjectContents(
@@ -2050,7 +2051,7 @@
''');
var projectDir = createProjectDir(projectContents);
var cliRunner = _createCli()
- .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+ .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!;
await cliRunner.run();
// The Dart source code should still be migrated.
assertProjectContents(projectDir, simpleProject(migrated: true, pubspecText:
@@ -2066,7 +2067,7 @@
var projectContents = simpleProject(pubspecText: 'not-a-map');
var projectDir = createProjectDir(projectContents);
var cliRunner = _createCli()
- .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+ .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!;
var message = await assertErrorExit(
cliRunner, () async => await cliRunner.run(),
withUsage: false);
@@ -2086,7 +2087,7 @@
''');
var projectDir = createProjectDir(projectContents);
var cliRunner = _createCli()
- .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+ .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!;
await cliRunner.run();
expect(logger.stdoutBuffer.toString(),
isNot(contains('Please run `dart pub get`')));
@@ -2119,7 +2120,7 @@
''');
var projectDir = createProjectDir(projectContents);
var cliRunner = _createCli()
- .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+ .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!;
await cliRunner.run();
expect(
logger.stdoutBuffer.toString(), contains('Please run `dart pub get`'));
@@ -2142,7 +2143,7 @@
test_pubspec_with_sdk_version_beta() async {
var projectDir = createProjectDir(simpleProject());
var cliRunner = _createCli(sdkVersion: '2.12.0-1.2.beta')
- .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+ .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!;
await cliRunner.run();
assertProjectContents(
projectDir, simpleProject(migrated: true, pubspecText: '''
@@ -2155,7 +2156,7 @@
test_pubspec_with_sdk_version_dev() async {
var projectDir = createProjectDir(simpleProject());
var cliRunner = _createCli(sdkVersion: '2.12.0-1.2.dev')
- .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+ .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!;
await cliRunner.run();
assertProjectContents(
projectDir, simpleProject(migrated: true, pubspecText: '''
@@ -2168,7 +2169,7 @@
test_pubspec_with_sdk_version_edge() async {
var projectDir = createProjectDir(simpleProject());
var cliRunner = _createCli(sdkVersion: '2.12.0-edge.1234567')
- .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+ .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!;
await cliRunner.run();
assertProjectContents(
projectDir, simpleProject(migrated: true, pubspecText: '''
@@ -2181,7 +2182,7 @@
test_pubspec_with_sdk_version_internal() async {
var projectDir = createProjectDir(simpleProject());
var cliRunner = _createCli(sdkVersion: '2.12.0-1234567')
- .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+ .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!;
await cliRunner.run();
assertProjectContents(
projectDir, simpleProject(migrated: true, pubspecText: '''
@@ -2206,7 +2207,7 @@
}
_MigrationCli _createCli(
- {List<String> nullSafePackages = const [], String sdkVersion}) {
+ {List<String> nullSafePackages = const [], String? sdkVersion}) {
mock_sdk.MockSdk(
resourceProvider: resourceProvider,
nullSafePackages: nullSafePackages,
@@ -2224,7 +2225,7 @@
expect(errorOutput, contains('Set the lower SDK constraint'));
}
- String _getHelpText({@required bool verbose}) {
+ String _getHelpText({required bool verbose}) {
var cliRunner = _createCli().decodeCommandLineArgs(_parseArgs(
['--${CommandLineOptions.helpFlag}', if (verbose) '--verbose']));
expect(cliRunner, isNull);
@@ -2233,9 +2234,8 @@
}
String _getPackageConfigText(
- {@required bool migrated,
- Map<String, bool> packagesMigrated = const {}}) {
- Object makePackageEntry(String name, bool migrated, {String rootUri}) {
+ {required bool migrated, Map<String, bool> packagesMigrated = const {}}) {
+ Object makePackageEntry(String name, bool migrated, {String? rootUri}) {
rootUri ??=
resourceProvider.pathContext.toUri(packagePath(name)).toString();
return {
@@ -2278,7 +2278,7 @@
class _MigrationCliTestPosix extends _MigrationCliTestBase
with _MigrationCliTestMethods {
@override
- final resourceProvider;
+ final MemoryResourceProvider resourceProvider;
_MigrationCliTestPosix()
: resourceProvider = MemoryResourceProvider(
@@ -2292,7 +2292,7 @@
class _MigrationCliTestWindows extends _MigrationCliTestBase
with _MigrationCliTestMethods {
@override
- final resourceProvider;
+ final MemoryResourceProvider resourceProvider;
_MigrationCliTestWindows()
: resourceProvider = MemoryResourceProvider(
diff --git a/pkg/nnbd_migration/test/migration_visitor_test_base.dart b/pkg/nnbd_migration/test/migration_visitor_test_base.dart
index f65f280..9a4294b 100644
--- a/pkg/nnbd_migration/test/migration_visitor_test_base.dart
+++ b/pkg/nnbd_migration/test/migration_visitor_test_base.dart
@@ -11,7 +11,6 @@
import 'package:analyzer/src/dart/element/type_system.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/utilities_dart.dart';
-import 'package:meta/meta.dart';
import 'package:nnbd_migration/instrumentation.dart';
import 'package:nnbd_migration/src/conditional_discard.dart';
import 'package:nnbd_migration/src/decorated_class_hierarchy.dart';
@@ -29,7 +28,7 @@
/// A [NodeMatcher] that matches any node, and records what node it matched to.
class AnyNodeMatcher extends _RecordingNodeMatcher {
@override
- bool matches(NullabilityNode node) {
+ bool matches(NullabilityNode? node) {
return true;
}
}
@@ -55,21 +54,21 @@
List<DecoratedType> positional = const [],
Map<String, DecoratedType> named = const {},
List<TypeParameterElement> typeFormals = const [],
- NullabilityNode node}) {
+ NullabilityNode? node}) {
int i = 0;
var parameters = required
.map((t) => ParameterElementImpl.synthetic(
- 'p${i++}', t.type, ParameterKind.REQUIRED))
+ 'p${i++}', t.type!, ParameterKind.REQUIRED))
.toList();
parameters.addAll(positional.map((t) => ParameterElementImpl.synthetic(
- 'p${i++}', t.type, ParameterKind.POSITIONAL)));
+ 'p${i++}', t.type!, ParameterKind.POSITIONAL)));
parameters.addAll(named.entries.map((e) => ParameterElementImpl.synthetic(
- e.key, e.value.type, ParameterKind.NAMED)));
+ e.key, e.value.type!, ParameterKind.NAMED)));
return DecoratedType(
FunctionTypeImpl(
typeFormals: typeFormals,
parameters: parameters,
- returnType: returnType.type,
+ returnType: returnType.type!,
nullabilitySuffix: NullabilitySuffix.star,
),
node ?? newNode(),
@@ -78,37 +77,37 @@
namedParameters: named);
}
- DecoratedType future(DecoratedType parameter, {NullabilityNode node}) {
+ DecoratedType future(DecoratedType parameter, {NullabilityNode? node}) {
return DecoratedType(
- typeProvider.futureType(parameter.type), node ?? newNode(),
+ typeProvider.futureType(parameter.type!), node ?? newNode(),
typeArguments: [parameter]);
}
- DecoratedType futureOr(DecoratedType parameter, {NullabilityNode node}) {
+ DecoratedType futureOr(DecoratedType parameter, {NullabilityNode? node}) {
return DecoratedType(
- typeProvider.futureOrType(parameter.type), node ?? newNode(),
+ typeProvider.futureOrType(parameter.type!), node ?? newNode(),
typeArguments: [parameter]);
}
- DecoratedType int_({NullabilityNode node}) =>
+ DecoratedType int_({NullabilityNode? node}) =>
DecoratedType(typeProvider.intType, node ?? newNode());
- DecoratedType iterable(DecoratedType elementType, {NullabilityNode node}) =>
+ DecoratedType iterable(DecoratedType elementType, {NullabilityNode? node}) =>
DecoratedType(
- typeProvider.iterableType(elementType.type), node ?? newNode(),
+ typeProvider.iterableType(elementType.type!), node ?? newNode(),
typeArguments: [elementType]);
- DecoratedType list(DecoratedType elementType, {NullabilityNode node}) =>
- DecoratedType(typeProvider.listType(elementType.type), node ?? newNode(),
+ DecoratedType list(DecoratedType elementType, {NullabilityNode? node}) =>
+ DecoratedType(typeProvider.listType(elementType.type!), node ?? newNode(),
typeArguments: [elementType]);
NullabilityNode newNode() => NullabilityNode.forTypeAnnotation(
NullabilityNodeTarget.text('node ${nodeId++}'));
- DecoratedType num_({NullabilityNode node}) =>
+ DecoratedType num_({NullabilityNode? node}) =>
DecoratedType(typeProvider.numType, node ?? newNode());
- DecoratedType object({NullabilityNode node}) =>
+ DecoratedType object({NullabilityNode? node}) =>
DecoratedType(typeProvider.objectType, node ?? newNode());
TypeParameterElement typeParameter(String name, DecoratedType bound) {
@@ -119,7 +118,7 @@
}
DecoratedType typeParameterType(TypeParameterElement typeParameter,
- {NullabilityNode node}) {
+ {NullabilityNode? node}) {
return DecoratedType(
typeParameter.instantiate(
nullabilitySuffix: NullabilitySuffix.star,
@@ -140,7 +139,7 @@
}
class EdgeBuilderTestBase extends MigrationVisitorTestBase {
- DecoratedClassHierarchy decoratedClassHierarchy;
+ DecoratedClassHierarchy? decoratedClassHierarchy;
/// Analyzes the given source code, producing constraint variables and
/// constraints for it.
@@ -174,9 +173,9 @@
/// Gets the transitive closure of all nodes with hard edges pointing to
/// never, plus never itself.
- Set<NullabilityNode> get neverClosure {
- var result = <NullabilityNode>{};
- var pending = <NullabilityNode>[graph.never];
+ Set<NullabilityNode?> get neverClosure {
+ var result = <NullabilityNode?>{};
+ var pending = <NullabilityNode?>[graph.never];
while (pending.isNotEmpty) {
var node = pending.removeLast();
if (result.add(node)) {
@@ -189,12 +188,12 @@
}
/// Gets the set of nodes with hard edges pointing to never.
- Set<NullabilityNode> get pointsToNever {
+ Set<NullabilityNode?> get pointsToNever {
return {for (var edge in getEdges(anyNode, graph.never)) edge.sourceNode};
}
/// Asserts that a dummy edge exists from [source] to always.
- NullabilityEdge assertDummyEdge(Object source) =>
+ NullabilityEdge assertDummyEdge(Object? source) =>
assertEdge(source, graph.always, hard: false, checkable: false);
/// Asserts that an edge exists with a node matching [source] and a node
@@ -203,12 +202,12 @@
/// [source] and [destination] are converted to [NodeMatcher] objects if they
/// aren't already. In practice this means that the caller can pass in either
/// a [NodeMatcher] or a [NullabilityNode].
- NullabilityEdge assertEdge(Object source, Object destination,
- {@required bool hard,
+ NullabilityEdge assertEdge(Object? source, Object? destination,
+ {required bool hard,
bool checkable = true,
bool isSetupAssignment = false,
Object guards = isEmpty,
- Object codeReference}) {
+ Object? codeReference}) {
var edges = getEdges(source, destination);
if (edges.isEmpty) {
fail('Expected edge $source -> $destination, found none');
@@ -233,7 +232,7 @@
/// [source] and [destination] are converted to [NodeMatcher] objects if they
/// aren't already. In practice this means that the caller can pass in either
/// a [NodeMatcher] or a [NullabilityNode].
- void assertNoEdge(Object source, Object destination) {
+ void assertNoEdge(Object? source, Object? destination) {
var edges = getEdges(source, destination);
if (edges.isNotEmpty) {
fail('Expected no edge $source -> $destination, found $edges');
@@ -245,7 +244,7 @@
/// [x] and [y] are converted to [NodeMatcher] objects if they aren't already.
/// In practice this means that the caller can pass in either a [NodeMatcher]
/// or a [NullabilityNode].
- void assertUnion(Object x, Object y) {
+ void assertUnion(Object? x, Object? y) {
var edges = getEdges(x, y);
for (var edge in edges) {
if (edge.isUnion) {
@@ -262,7 +261,7 @@
/// [source] and [destination] are converted to [NodeMatcher] objects if they
/// aren't already. In practice this means that the caller can pass in either
/// a [NodeMatcher] or a [NullabilityNode].
- List<NullabilityEdge> getEdges(Object source, Object destination) {
+ List<NullabilityEdge> getEdges(Object? source, Object? destination) {
var sourceMatcher = NodeMatcher(source);
var destinationMatcher = NodeMatcher(destination);
var result = <NullabilityEdge>[];
@@ -278,7 +277,7 @@
}
/// Returns a [NodeMatcher] that matches any node in the given set.
- NodeSetMatcher inSet(Set<NullabilityNode> nodes) => NodeSetMatcher(nodes);
+ NodeSetMatcher inSet(Set<NullabilityNode?> nodes) => NodeSetMatcher(nodes);
/// Creates a [NodeMatcher] matching a substitution node whose inner and outer
/// nodes match [inner] and [outer].
@@ -286,7 +285,7 @@
/// [inner] and [outer] are converted to [NodeMatcher] objects if they aren't
/// already. In practice this means that the caller can pass in either a
/// [NodeMatcher] or a [NullabilityNode].
- NodeMatcher substitutionNode(Object inner, Object outer) =>
+ NodeMatcher substitutionNode(Object? inner, Object? outer) =>
_SubstitutionNodeMatcher(NodeMatcher(inner), NodeMatcher(outer));
}
@@ -294,7 +293,7 @@
class InstrumentedVariables extends Variables {
final _conditionalDiscard = <AstNode, ConditionalDiscard>{};
- final _decoratedExpressionTypes = <Expression, DecoratedType>{};
+ final _decoratedExpressionTypes = <Expression, DecoratedType?>{};
final _expressionChecks = <Expression, ExpressionChecksOrigin>{};
@@ -303,32 +302,32 @@
: super(graph, typeProvider, getLineInfo);
/// Gets the [ExpressionChecks] associated with the given [expression].
- ExpressionChecksOrigin checkExpression(Expression expression) =>
+ ExpressionChecksOrigin? checkExpression(Expression expression) =>
_expressionChecks[_normalizeExpression(expression)];
/// Gets the [conditionalDiscard] associated with the given [expression].
- ConditionalDiscard conditionalDiscard(AstNode node) =>
+ ConditionalDiscard? conditionalDiscard(AstNode node) =>
_conditionalDiscard[node];
/// Gets the [DecoratedType] associated with the given [expression].
- DecoratedType decoratedExpressionType(Expression expression) =>
+ DecoratedType? decoratedExpressionType(Expression expression) =>
_decoratedExpressionTypes[_normalizeExpression(expression)];
@override
void recordConditionalDiscard(
- Source source, AstNode node, ConditionalDiscard conditionalDiscard) {
+ Source? source, AstNode node, ConditionalDiscard conditionalDiscard) {
_conditionalDiscard[node] = conditionalDiscard;
super.recordConditionalDiscard(source, node, conditionalDiscard);
}
- void recordDecoratedExpressionType(Expression node, DecoratedType type) {
+ void recordDecoratedExpressionType(Expression node, DecoratedType? type) {
super.recordDecoratedExpressionType(node, type);
_decoratedExpressionTypes[_normalizeExpression(node)] = type;
}
@override
void recordExpressionChecks(
- Source source, Expression expression, ExpressionChecksOrigin origin) {
+ Source? source, Expression expression, ExpressionChecksOrigin origin) {
super.recordExpressionChecks(source, expression, origin);
_expressionChecks[_normalizeExpression(expression)] = origin;
}
@@ -336,14 +335,14 @@
/// Unwraps any parentheses surrounding [expression].
Expression _normalizeExpression(Expression expression) {
while (expression is ParenthesizedExpression) {
- expression = (expression as ParenthesizedExpression).expression;
+ expression = expression.expression;
}
return expression;
}
}
class MigrationVisitorTestBase extends AbstractSingleUnitTest with EdgeTester {
- InstrumentedVariables variables;
+ InstrumentedVariables? variables;
final NullabilityGraphForTesting graph;
@@ -365,50 +364,51 @@
Future<CompilationUnit> analyze(String code) async {
await resolveTestUnit(code);
variables = InstrumentedVariables(graph, typeProvider, getLineInfo);
- testUnit.accept(NodeBuilder(
+ testUnit!.accept(NodeBuilder(
variables, testSource, null, graph, typeProvider, getLineInfo));
- return testUnit;
+ return testUnit!;
}
/// Gets the [DecoratedType] associated with the constructor declaration whose
/// name matches [search].
- DecoratedType decoratedConstructorDeclaration(String search) => variables
- .decoratedElementType(findNode.constructor(search).declaredElement);
+ DecoratedType decoratedConstructorDeclaration(String search) => variables!
+ .decoratedElementType(findNode.constructor(search).declaredElement!);
- Map<ClassElement, DecoratedType> decoratedDirectSupertypes(String name) {
- return variables.decoratedDirectSupertypes(findElement.classOrMixin(name));
+ Map<ClassElement, DecoratedType?> decoratedDirectSupertypes(String name) {
+ return variables!.decoratedDirectSupertypes(findElement.classOrMixin(name));
}
/// Gets the [DecoratedType] associated with the generic function type
/// annotation whose text is [text].
DecoratedType decoratedGenericFunctionTypeAnnotation(String text) {
- return variables.decoratedTypeAnnotation(
+ return variables!.decoratedTypeAnnotation(
testSource, findNode.genericFunctionType(text));
}
/// Gets the [DecoratedType] associated with the method declaration whose
/// name matches [search].
- DecoratedType decoratedMethodType(String search) => variables
- .decoratedElementType(findNode.methodDeclaration(search).declaredElement);
+ DecoratedType decoratedMethodType(String search) =>
+ variables!.decoratedElementType(
+ findNode.methodDeclaration(search).declaredElement!);
/// Gets the [DecoratedType] associated with the type annotation whose text
/// is [text].
DecoratedType decoratedTypeAnnotation(String text) {
- return variables.decoratedTypeAnnotation(
- testSource, findNode.typeAnnotation(text));
+ return variables!
+ .decoratedTypeAnnotation(testSource, findNode.typeAnnotation(text));
}
/// Gets the [ConditionalDiscard] information associated with the collection
/// element whose text is [text].
- ConditionalDiscard elementDiscard(String text) {
- return variables.conditionalDiscard(findNode.collectionElement(text));
+ ConditionalDiscard? elementDiscard(String text) {
+ return variables!.conditionalDiscard(findNode.collectionElement(text));
}
/// Returns a [Matcher] that matches a [CodeReference] pointing to the given
/// file [offset], with the given [function] name.
TypeMatcher<CodeReference> matchCodeRef(
- {@required int offset, @required String function}) {
- var location = testUnit.lineInfo.getLocation(offset);
+ {required int offset, required String function}) {
+ var location = testUnit!.lineInfo!.getLocation(offset);
return TypeMatcher<CodeReference>()
.having((cr) => cr.line, 'line', location.lineNumber)
.having((cr) => cr.column, 'column', location.columnNumber)
@@ -422,8 +422,8 @@
/// Gets the [ConditionalDiscard] information associated with the statement
/// whose text is [text].
- ConditionalDiscard statementDiscard(String text) {
- return variables.conditionalDiscard(findNode.statement(text));
+ ConditionalDiscard? statementDiscard(String text) {
+ return variables!.conditionalDiscard(findNode.statement(text));
}
void tearDown() {
@@ -435,26 +435,26 @@
/// Abstract base class representing a thing that can be matched against
/// nullability nodes.
abstract class NodeMatcher {
- factory NodeMatcher(Object expectation) {
+ factory NodeMatcher(Object? expectation) {
if (expectation is NodeMatcher) return expectation;
if (expectation is NullabilityNode) return _ExactNodeMatcher(expectation);
fail(
'Unclear how to match node expectation of type ${expectation.runtimeType}');
}
- void matched(NullabilityNode node);
+ void matched(NullabilityNode? node);
- bool matches(NullabilityNode node);
+ bool matches(NullabilityNode? node);
}
/// A [NodeMatcher] that matches any node contained in the given set.
class NodeSetMatcher extends _RecordingNodeMatcher {
- final Set<NullabilityNode> _targetSet;
+ final Set<NullabilityNode?> _targetSet;
NodeSetMatcher(this._targetSet);
@override
- bool matches(NullabilityNode node) => _targetSet.contains(node);
+ bool matches(NullabilityNode? node) => _targetSet.contains(node);
}
/// A [NodeMatcher] that matches exactly one node.
@@ -464,20 +464,20 @@
_ExactNodeMatcher(this._expectation);
@override
- void matched(NullabilityNode node) {}
+ void matched(NullabilityNode? node) {}
@override
- bool matches(NullabilityNode node) => node == _expectation;
+ bool matches(NullabilityNode? node) => node == _expectation;
}
/// Base class for [NodeMatcher]s that remember which nodes were matched.
abstract class _RecordingNodeMatcher implements NodeMatcher {
- final List<NullabilityNode> _matchingNodes = [];
+ final List<NullabilityNode?> _matchingNodes = [];
- NullabilityNode get matchingNode => _matchingNodes.single;
+ NullabilityNode? get matchingNode => _matchingNodes.single;
@override
- void matched(NullabilityNode node) {
+ void matched(NullabilityNode? node) {
_matchingNodes.add(node);
}
}
@@ -491,7 +491,7 @@
_SubstitutionNodeMatcher(this.inner, this.outer);
@override
- void matched(NullabilityNode node) {
+ void matched(NullabilityNode? node) {
if (node is NullabilityNodeForSubstitution) {
inner.matched(node.innerNode);
outer.matched(node.outerNode);
@@ -503,7 +503,7 @@
}
@override
- bool matches(NullabilityNode node) {
+ bool matches(NullabilityNode? node) {
return node is NullabilityNodeForSubstitution &&
inner.matches(node.innerNode) &&
outer.matches(node.outerNode);
diff --git a/pkg/nnbd_migration/test/node_builder_test.dart b/pkg/nnbd_migration/test/node_builder_test.dart
index 54d4aa1..22ea76b 100644
--- a/pkg/nnbd_migration/test/node_builder_test.dart
+++ b/pkg/nnbd_migration/test/node_builder_test.dart
@@ -23,12 +23,12 @@
/// Gets the [DecoratedType] associated with the function declaration whose
/// name matches [search].
DecoratedType decoratedFunctionType(String search) =>
- variables.decoratedElementType(
- findNode.functionDeclaration(search).declaredElement);
+ variables!.decoratedElementType(
+ findNode.functionDeclaration(search).declaredElement!);
- DecoratedType decoratedTypeParameterBound(String search) =>
- variables.decoratedTypeParameterBound(
- findNode.typeParameter(search).declaredElement);
+ DecoratedType? decoratedTypeParameterBound(String search) =>
+ variables!.decoratedTypeParameterBound(
+ findNode.typeParameter(search).declaredElement!);
Future<void> test_catch_clause_with_stacktrace_with_on() async {
await analyze('''
@@ -37,10 +37,10 @@
}
''');
var exceptionType =
- variables.decoratedElementType(findNode.simple('ex').staticElement);
+ variables!.decoratedElementType(findNode.simple('ex').staticElement!);
expect(exceptionType.node, TypeMatcher<NullabilityNodeMutable>());
var stackTraceType =
- variables.decoratedElementType(findNode.simple('st').staticElement);
+ variables!.decoratedElementType(findNode.simple('st').staticElement!);
assertEdge(stackTraceType.node, never, hard: true, checkable: false);
}
@@ -51,10 +51,10 @@
}
''');
var exceptionType =
- variables.decoratedElementType(findNode.simple('ex').staticElement);
- expect(exceptionType.node.isImmutable, false);
+ variables!.decoratedElementType(findNode.simple('ex').staticElement!);
+ expect(exceptionType.node!.isImmutable, false);
var stackTraceType =
- variables.decoratedElementType(findNode.simple('st').staticElement);
+ variables!.decoratedElementType(findNode.simple('st').staticElement!);
assertEdge(stackTraceType.node, never, hard: true, checkable: false);
}
@@ -75,7 +75,7 @@
}
''');
var exceptionType =
- variables.decoratedElementType(findNode.simple('ex').staticElement);
+ variables!.decoratedElementType(findNode.simple('ex').staticElement!);
expect(exceptionType.node, TypeMatcher<NullabilityNodeMutable>());
}
@@ -86,8 +86,8 @@
}
''');
var exceptionType =
- variables.decoratedElementType(findNode.simple('ex').staticElement);
- expect(exceptionType.node.isImmutable, false);
+ variables!.decoratedElementType(findNode.simple('ex').staticElement!);
+ expect(exceptionType.node!.isImmutable, false);
}
Future<void> test_class_alias_synthetic_constructors_no_parameters() async {
@@ -102,19 +102,19 @@
var constructors = findElement.class_('D').constructors;
expect(constructors, hasLength(2));
var a = findElement.constructor('a', of: 'D');
- var aType = variables.decoratedElementType(a);
- _assertType(aType.type, 'D Function()');
+ var aType = variables!.decoratedElementType(a);
+ _assertType(aType.type!, 'D Function()');
expect(aType.node, same(never));
expect(aType.typeArguments, isEmpty);
- _assertType(aType.returnType.type, 'D');
- expect(aType.returnType.node, same(never));
+ _assertType(aType.returnType!.type!, 'D');
+ expect(aType.returnType!.node, same(never));
var b = findElement.constructor('b', of: 'D');
- var bType = variables.decoratedElementType(b);
- _assertType(bType.type, 'D Function()');
+ var bType = variables!.decoratedElementType(b);
+ _assertType(bType.type!, 'D Function()');
expect(bType.node, same(never));
expect(bType.typeArguments, isEmpty);
- _assertType(bType.returnType.type, 'D');
- expect(bType.returnType.node, same(never));
+ _assertType(bType.returnType!.type!, 'D');
+ expect(bType.returnType!.node, same(never));
}
Future<void> test_class_alias_synthetic_constructors_with_parameters() async {
@@ -131,56 +131,56 @@
var constructors = findElement.class_('D').constructors;
expect(constructors, hasLength(4));
var a = findElement.constructor('a', of: 'D');
- var aType = variables.decoratedElementType(a);
- _assertType(aType.type, 'D Function(int)');
+ var aType = variables!.decoratedElementType(a);
+ _assertType(aType.type!, 'D Function(int)');
expect(aType.node, same(never));
expect(aType.typeArguments, isEmpty);
- _assertType(aType.returnType.type, 'D');
- expect(aType.returnType.node, same(never));
+ _assertType(aType.returnType!.type!, 'D');
+ expect(aType.returnType!.node, same(never));
expect(aType.positionalParameters, hasLength(1));
- _assertType(aType.positionalParameters[0].type, 'int');
- expect(aType.positionalParameters[0].node,
+ _assertType(aType.positionalParameters![0]!.type!, 'int');
+ expect(aType.positionalParameters![0]!.node,
TypeMatcher<NullabilityNodeMutable>());
expect(aType.namedParameters, isEmpty);
var b = findElement.constructor('b', of: 'D');
- var bType = variables.decoratedElementType(b);
- _assertType(bType.type, 'D Function([int])');
+ var bType = variables!.decoratedElementType(b);
+ _assertType(bType.type!, 'D Function([int])');
expect(bType.node, same(never));
expect(bType.typeArguments, isEmpty);
- _assertType(bType.returnType.type, 'D');
- expect(bType.returnType.node, same(never));
+ _assertType(bType.returnType!.type!, 'D');
+ expect(bType.returnType!.node, same(never));
expect(bType.positionalParameters, hasLength(1));
- _assertType(bType.positionalParameters[0].type, 'int');
- expect(bType.positionalParameters[0].node,
+ _assertType(bType.positionalParameters![0]!.type!, 'int');
+ expect(bType.positionalParameters![0]!.node,
TypeMatcher<NullabilityNodeMutable>());
expect(bType.namedParameters, isEmpty);
var c = findElement.constructor('c', of: 'D');
- var cType = variables.decoratedElementType(c);
- _assertType(cType.type, 'D Function({int i})');
+ var cType = variables!.decoratedElementType(c);
+ _assertType(cType.type!, 'D Function({int i})');
expect(cType.node, same(never));
expect(cType.typeArguments, isEmpty);
- _assertType(cType.returnType.type, 'D');
- expect(cType.returnType.node, same(never));
+ _assertType(cType.returnType!.type!, 'D');
+ expect(cType.returnType!.node, same(never));
expect(cType.positionalParameters, isEmpty);
expect(cType.namedParameters, hasLength(1));
expect(cType.namedParameters, contains('i'));
- _assertType(cType.namedParameters['i'].type, 'int');
- expect(
- cType.namedParameters['i'].node, TypeMatcher<NullabilityNodeMutable>());
+ _assertType(cType.namedParameters!['i']!.type!, 'int');
+ expect(cType.namedParameters!['i']!.node,
+ TypeMatcher<NullabilityNodeMutable>());
var d = findElement.constructor('d', of: 'D');
- var dType = variables.decoratedElementType(d);
- _assertType(dType.type, 'D Function(List<int>)');
+ var dType = variables!.decoratedElementType(d);
+ _assertType(dType.type!, 'D Function(List<int>)');
expect(dType.node, same(never));
expect(dType.typeArguments, isEmpty);
- _assertType(dType.returnType.type, 'D');
- expect(dType.returnType.node, same(never));
+ _assertType(dType.returnType!.type!, 'D');
+ expect(dType.returnType!.node, same(never));
expect(dType.positionalParameters, hasLength(1));
- _assertType(dType.positionalParameters[0].type, 'List<int>');
- expect(dType.positionalParameters[0].node,
+ _assertType(dType.positionalParameters![0]!.type!, 'List<int>');
+ expect(dType.positionalParameters![0]!.node,
TypeMatcher<NullabilityNodeMutable>());
- expect(dType.positionalParameters[0].typeArguments, hasLength(1));
- _assertType(dType.positionalParameters[0].typeArguments[0].type, 'int');
- expect(dType.positionalParameters[0].typeArguments[0].node,
+ expect(dType.positionalParameters![0]!.typeArguments, hasLength(1));
+ _assertType(dType.positionalParameters![0]!.typeArguments[0]!.type!, 'int');
+ expect(dType.positionalParameters![0]!.typeArguments[0]!.node,
TypeMatcher<NullabilityNodeMutable>());
expect(dType.namedParameters, isEmpty);
}
@@ -195,20 +195,20 @@
class D<U> = C<U> with M;
''');
var dConstructor = findElement.unnamedConstructor('D');
- var dConstructorType = variables.decoratedElementType(dConstructor);
- _assertType(dConstructorType.type, 'D<U> Function(U)');
+ var dConstructorType = variables!.decoratedElementType(dConstructor);
+ _assertType(dConstructorType.type!, 'D<U> Function(U)');
expect(dConstructorType.node, same(never));
expect(dConstructorType.typeFormals, isEmpty);
- _assertType(dConstructorType.returnType.type, 'D<U>');
- expect(dConstructorType.returnType.node, same(never));
- var typeArguments = dConstructorType.returnType.typeArguments;
+ _assertType(dConstructorType.returnType!.type!, 'D<U>');
+ expect(dConstructorType.returnType!.node, same(never));
+ var typeArguments = dConstructorType.returnType!.typeArguments;
expect(typeArguments, hasLength(1));
- _assertType(typeArguments[0].type, 'U');
- expect(typeArguments[0].node, same(never));
- var dParams = dConstructorType.positionalParameters;
+ _assertType(typeArguments[0]!.type!, 'U');
+ expect(typeArguments[0]!.node, same(never));
+ var dParams = dConstructorType.positionalParameters!;
expect(dParams, hasLength(1));
- _assertType(dParams[0].type, 'U');
- expect(dParams[0].node, TypeMatcher<NullabilityNodeMutable>());
+ _assertType(dParams[0]!.type!, 'U');
+ expect(dParams[0]!.node, TypeMatcher<NullabilityNodeMutable>());
}
Future<void> test_class_with_default_constructor() async {
@@ -217,11 +217,11 @@
''');
var defaultConstructor = findElement.class_('C').constructors.single;
var decoratedConstructorType =
- variables.decoratedElementType(defaultConstructor);
- _assertType(decoratedConstructorType.type, 'C Function()');
+ variables!.decoratedElementType(defaultConstructor);
+ _assertType(decoratedConstructorType.type!, 'C Function()');
expect(decoratedConstructorType.node, same(never));
- _assertType(decoratedConstructorType.returnType.type, 'C');
- expect(decoratedConstructorType.returnType.node, same(never));
+ _assertType(decoratedConstructorType.returnType!.type!, 'C');
+ expect(decoratedConstructorType.returnType!.node, same(never));
}
Future<void> test_class_with_default_constructor_generic() async {
@@ -230,18 +230,18 @@
''');
var defaultConstructor = findElement.class_('C').constructors.single;
var decoratedConstructorType =
- variables.decoratedElementType(defaultConstructor);
- _assertType(decoratedConstructorType.type, 'C<T, U> Function()');
+ variables!.decoratedElementType(defaultConstructor);
+ _assertType(decoratedConstructorType.type!, 'C<T, U> Function()');
expect(decoratedConstructorType.node, same(never));
expect(decoratedConstructorType.typeArguments, isEmpty);
- var returnType = decoratedConstructorType.returnType;
- _assertType(returnType.type, 'C<T, U>');
+ var returnType = decoratedConstructorType.returnType!;
+ _assertType(returnType.type!, 'C<T, U>');
expect(returnType.node, same(never));
expect(returnType.typeArguments, hasLength(2));
- _assertType(returnType.typeArguments[0].type, 'T');
- expect(returnType.typeArguments[0].node, same(never));
- _assertType(returnType.typeArguments[1].type, 'U');
- expect(returnType.typeArguments[1].node, same(never));
+ _assertType(returnType.typeArguments[0]!.type!, 'T');
+ expect(returnType.typeArguments[0]!.node, same(never));
+ _assertType(returnType.typeArguments[1]!.type!, 'U');
+ expect(returnType.typeArguments[1]!.node, same(never));
}
Future<void> test_constructor_factory() async {
@@ -251,7 +251,7 @@
factory C() => C._();
}
''');
- var decoratedType = decoratedConstructorDeclaration('C(').returnType;
+ var decoratedType = decoratedConstructorDeclaration('C(').returnType!;
expect(decoratedType.node, same(never));
}
@@ -276,7 +276,7 @@
C();
}
''');
- var decoratedType = decoratedConstructorDeclaration('C(').returnType;
+ var decoratedType = decoratedConstructorDeclaration('C(').returnType!;
expect(decoratedType.node, same(never));
}
@@ -297,13 +297,13 @@
class D<V> extends C<int, V> {}
''');
var types = decoratedDirectSupertypes('D');
- var decorated = types[findElement.class_('C')];
- _assertType(decorated.type, 'C<int, V>');
+ var decorated = types[findElement.class_('C')]!;
+ _assertType(decorated.type!, 'C<int, V>');
expect(decorated.node, same(never));
expect(decorated.typeArguments, hasLength(2));
- expect(decorated.typeArguments[0].node,
+ expect(decorated.typeArguments[0]!.node,
same(decoratedTypeAnnotation('int').node));
- expect(decorated.typeArguments[1].node,
+ expect(decorated.typeArguments[1]!.node,
same(decoratedTypeAnnotation('V> {').node));
}
@@ -312,8 +312,8 @@
class C<T, U> {}
''');
var types = decoratedDirectSupertypes('C');
- var decorated = types[typeProvider.objectType.element];
- _assertType(decorated.type, 'Object');
+ var decorated = types[typeProvider.objectType.element]!;
+ _assertType(decorated.type!, 'Object');
assertEdge(decorated.node, never, hard: true, checkable: false);
expect(decorated.typeArguments, isEmpty);
}
@@ -324,13 +324,13 @@
class D<V> implements C<int, V> {}
''');
var types = decoratedDirectSupertypes('D');
- var decorated = types[findElement.class_('C')];
- _assertType(decorated.type, 'C<int, V>');
+ var decorated = types[findElement.class_('C')]!;
+ _assertType(decorated.type!, 'C<int, V>');
expect(decorated.node, same(never));
expect(decorated.typeArguments, hasLength(2));
- expect(decorated.typeArguments[0].node,
+ expect(decorated.typeArguments[0]!.node,
same(decoratedTypeAnnotation('int').node));
- expect(decorated.typeArguments[1].node,
+ expect(decorated.typeArguments[1]!.node,
same(decoratedTypeAnnotation('V> {').node));
}
@@ -340,13 +340,13 @@
class D<V> extends Object with C<int, V> {}
''');
var types = decoratedDirectSupertypes('D');
- var decorated = types[findElement.class_('C')];
- _assertType(decorated.type, 'C<int, V>');
+ var decorated = types[findElement.class_('C')]!;
+ _assertType(decorated.type!, 'C<int, V>');
expect(decorated.node, same(never));
expect(decorated.typeArguments, hasLength(2));
- expect(decorated.typeArguments[0].node,
+ expect(decorated.typeArguments[0]!.node,
same(decoratedTypeAnnotation('int').node));
- expect(decorated.typeArguments[1].node,
+ expect(decorated.typeArguments[1]!.node,
same(decoratedTypeAnnotation('V> {').node));
}
@@ -357,13 +357,13 @@
class D<V> = C<int, V> with M;
''');
var types = decoratedDirectSupertypes('D');
- var decorated = types[findElement.class_('C')];
- _assertType(decorated.type, 'C<int, V>');
+ var decorated = types[findElement.class_('C')]!;
+ _assertType(decorated.type!, 'C<int, V>');
expect(decorated.node, same(never));
expect(decorated.typeArguments, hasLength(2));
- expect(decorated.typeArguments[0].node,
+ expect(decorated.typeArguments[0]!.node,
same(decoratedTypeAnnotation('int').node));
- expect(decorated.typeArguments[1].node,
+ expect(decorated.typeArguments[1]!.node,
same(decoratedTypeAnnotation('V> w').node));
}
@@ -374,13 +374,13 @@
class D<V> = Object with M implements C<int, V>;
''');
var types = decoratedDirectSupertypes('D');
- var decorated = types[findElement.class_('C')];
- _assertType(decorated.type, 'C<int, V>');
+ var decorated = types[findElement.class_('C')]!;
+ _assertType(decorated.type!, 'C<int, V>');
expect(decorated.node, same(never));
expect(decorated.typeArguments, hasLength(2));
- expect(decorated.typeArguments[0].node,
+ expect(decorated.typeArguments[0]!.node,
same(decoratedTypeAnnotation('int').node));
- expect(decorated.typeArguments[1].node,
+ expect(decorated.typeArguments[1]!.node,
same(decoratedTypeAnnotation('V>;').node));
}
@@ -390,13 +390,13 @@
class D<V> = Object with C<int, V>;
''');
var types = decoratedDirectSupertypes('D');
- var decorated = types[findElement.class_('C')];
- _assertType(decorated.type, 'C<int, V>');
+ var decorated = types[findElement.class_('C')]!;
+ _assertType(decorated.type!, 'C<int, V>');
expect(decorated.node, same(never));
expect(decorated.typeArguments, hasLength(2));
- expect(decorated.typeArguments[0].node,
+ expect(decorated.typeArguments[0]!.node,
same(decoratedTypeAnnotation('int').node));
- expect(decorated.typeArguments[1].node,
+ expect(decorated.typeArguments[1]!.node,
same(decoratedTypeAnnotation('V>;').node));
}
@@ -405,11 +405,11 @@
abstract class D<V> extends Iterable<V> {}
''');
var types = decoratedDirectSupertypes('D');
- var super_ = types.values.single;
- _assertType(super_.type, 'Iterable<V>');
+ var super_ = types.values.single!;
+ _assertType(super_.type!, 'Iterable<V>');
expect(super_.node, same(never));
expect(super_.typeArguments, hasLength(1));
- expect(super_.typeArguments[0].node,
+ expect(super_.typeArguments[0]!.node,
same(decoratedTypeAnnotation('V> {').node));
}
@@ -418,8 +418,8 @@
mixin C<T, U> {}
''');
var types = decoratedDirectSupertypes('C');
- var decorated = types[typeProvider.objectType.element];
- _assertType(decorated.type, 'Object');
+ var decorated = types[typeProvider.objectType.element]!;
+ _assertType(decorated.type!, 'Object');
assertEdge(decorated.node, never, hard: true, checkable: false);
expect(decorated.typeArguments, isEmpty);
}
@@ -430,13 +430,13 @@
mixin D<V> implements C<int, V> {}
''');
var types = decoratedDirectSupertypes('D');
- var decorated = types[findElement.class_('C')];
- _assertType(decorated.type, 'C<int, V>');
+ var decorated = types[findElement.class_('C')]!;
+ _assertType(decorated.type!, 'C<int, V>');
expect(decorated.node, same(never));
expect(decorated.typeArguments, hasLength(2));
- expect(decorated.typeArguments[0].node,
+ expect(decorated.typeArguments[0]!.node,
same(decoratedTypeAnnotation('int').node));
- expect(decorated.typeArguments[1].node,
+ expect(decorated.typeArguments[1]!.node,
same(decoratedTypeAnnotation('V> {').node));
}
@@ -446,19 +446,19 @@
mixin D<V> on C<int, V> {}
''');
var types = decoratedDirectSupertypes('D');
- var decorated = types[findElement.class_('C')];
- _assertType(decorated.type, 'C<int, V>');
+ var decorated = types[findElement.class_('C')]!;
+ _assertType(decorated.type!, 'C<int, V>');
expect(decorated.node, same(never));
expect(decorated.typeArguments, hasLength(2));
- expect(decorated.typeArguments[0].node,
+ expect(decorated.typeArguments[0]!.node,
same(decoratedTypeAnnotation('int').node));
- expect(decorated.typeArguments[1].node,
+ expect(decorated.typeArguments[1]!.node,
same(decoratedTypeAnnotation('V> {').node));
}
Future<void> test_displayName_castType() async {
await analyze('f(x) => x as int;');
- expect(decoratedTypeAnnotation('int').node.displayName,
+ expect(decoratedTypeAnnotation('int').node!.displayName,
'cast type (test.dart:1:14)');
}
@@ -469,7 +469,7 @@
}
class D<T> implements C {}
''');
- expect(decoratedTypeAnnotation('int').node.displayName,
+ expect(decoratedTypeAnnotation('int').node!.displayName,
'type argument 0 of constructed type (test.dart:2:19)');
}
@@ -482,9 +482,9 @@
}
''');
expect(
- variables
- .decoratedElementType(findNode.simple('e)').staticElement)
- .node
+ variables!
+ .decoratedElementType(findNode.simple('e)').staticElement!)
+ .node!
.displayName,
'f.e (test.dart:4:5)');
}
@@ -497,7 +497,7 @@
} on String {}
}
''');
- expect(decoratedTypeAnnotation('String').node.displayName,
+ expect(decoratedTypeAnnotation('String').node!.displayName,
'exception type (test.dart:4:8)');
}
@@ -509,80 +509,80 @@
} on String catch (s) {}
}
''');
- expect(decoratedTypeAnnotation('String').node.displayName,
+ expect(decoratedTypeAnnotation('String').node!.displayName,
'f.s (test.dart:4:8)');
}
Future<void> test_displayName_explicitParameterType_named() async {
await analyze('void f({int x, int y}) {}');
- expect(decoratedTypeAnnotation('int x').node.displayName,
+ expect(decoratedTypeAnnotation('int x').node!.displayName,
'parameter x of f (test.dart:1:9)');
- expect(decoratedTypeAnnotation('int y').node.displayName,
+ expect(decoratedTypeAnnotation('int y').node!.displayName,
'parameter y of f (test.dart:1:16)');
}
Future<void> test_displayName_explicitParameterType_positional() async {
await analyze('void f(int x, int y, [int z]) {}');
- expect(decoratedTypeAnnotation('int x').node.displayName,
+ expect(decoratedTypeAnnotation('int x').node!.displayName,
'parameter 0 of f (test.dart:1:8)');
- expect(decoratedTypeAnnotation('int y').node.displayName,
+ expect(decoratedTypeAnnotation('int y').node!.displayName,
'parameter 1 of f (test.dart:1:15)');
- expect(decoratedTypeAnnotation('int z').node.displayName,
+ expect(decoratedTypeAnnotation('int z').node!.displayName,
'parameter 2 of f (test.dart:1:23)');
}
Future<void> test_displayName_extendedType() async {
await analyze('extension E on int {}');
- expect(decoratedTypeAnnotation('int').node.displayName,
+ expect(decoratedTypeAnnotation('int').node!.displayName,
'extended type (test.dart:1:16)');
}
Future<void> test_displayName_field() async {
await analyze('class C { int x; }');
- expect(decoratedTypeAnnotation('int').node.displayName,
+ expect(decoratedTypeAnnotation('int').node!.displayName,
'C.x (test.dart:1:11)');
}
Future<void> test_displayName_for_loop_variable() async {
await analyze('f(List<int> x) { for (int y in x) {} }');
- expect(decoratedTypeAnnotation('int y').node.displayName,
+ expect(decoratedTypeAnnotation('int y').node!.displayName,
'f.y (test.dart:1:23)');
}
Future<void>
test_displayName_functionExpressionInvocation_type_argument() async {
await analyze('f(g) => g<int>();');
- expect(decoratedTypeAnnotation('int').node.displayName,
+ expect(decoratedTypeAnnotation('int').node!.displayName,
'type argument (test.dart:1:11)');
}
Future<void> test_displayName_listElementType() async {
await analyze('f() => <int>[];');
- expect(decoratedTypeAnnotation('int').node.displayName,
+ expect(decoratedTypeAnnotation('int').node!.displayName,
'list element type (test.dart:1:9)');
}
Future<void> test_displayName_mapKeyType() async {
await analyze('f() => <int, String>{};');
- expect(decoratedTypeAnnotation('int').node.displayName,
+ expect(decoratedTypeAnnotation('int').node!.displayName,
'map key type (test.dart:1:9)');
}
Future<void> test_displayName_mapValueType() async {
await analyze('f() => <int, String>{};');
- expect(decoratedTypeAnnotation('String').node.displayName,
+ expect(decoratedTypeAnnotation('String').node!.displayName,
'map value type (test.dart:1:14)');
}
Future<void> test_displayName_methodInvocation_type_argument() async {
await analyze('f(x) => x.g<int>();');
- expect(decoratedTypeAnnotation('int').node.displayName,
+ expect(decoratedTypeAnnotation('int').node!.displayName,
'type argument (test.dart:1:13)');
}
Future<void> test_displayName_setElementType() async {
await analyze('f() => <int>{};');
- expect(decoratedTypeAnnotation('int').node.displayName,
+ expect(decoratedTypeAnnotation('int').node!.displayName,
'set element type (test.dart:1:9)');
}
@@ -591,73 +591,73 @@
class C<T> {}
class D extends C<int> {}
''');
- expect(decoratedTypeAnnotation('int').node.displayName,
+ expect(decoratedTypeAnnotation('int').node!.displayName,
'type argument 0 of supertype of D (test.dart:2:19)');
}
Future<void> test_displayName_testedType() async {
await analyze('f(x) => x is int;');
- expect(decoratedTypeAnnotation('int').node.displayName,
+ expect(decoratedTypeAnnotation('int').node!.displayName,
'tested type (test.dart:1:14)');
}
Future<void> test_displayName_typeArgument() async {
await analyze('var x = <List<int>>[];');
- expect(decoratedTypeAnnotation('int').node.displayName,
+ expect(decoratedTypeAnnotation('int').node!.displayName,
'type argument 0 of list element type (test.dart:1:15)');
}
Future<void> test_displayName_typedef_new_parameter() async {
await analyze('typedef F = void Function(int x);');
- expect(decoratedTypeAnnotation('int').node.displayName,
+ expect(decoratedTypeAnnotation('int').node!.displayName,
'parameter 0 of F (test.dart:1:27)');
}
Future<void> test_displayName_typedef_new_returnType() async {
await analyze('typedef F = int Function();');
- expect(decoratedTypeAnnotation('int').node.displayName,
+ expect(decoratedTypeAnnotation('int').node!.displayName,
'return type of F (test.dart:1:13)');
}
Future<void> test_displayName_typedef_old_parameter() async {
await analyze('typedef void F(int x);');
- expect(decoratedTypeAnnotation('int').node.displayName,
+ expect(decoratedTypeAnnotation('int').node!.displayName,
'parameter 0 of F (test.dart:1:16)');
}
Future<void> test_displayName_typedef_old_returnType() async {
await analyze('typedef int F();');
- expect(decoratedTypeAnnotation('int').node.displayName,
+ expect(decoratedTypeAnnotation('int').node!.displayName,
'return type of F (test.dart:1:9)');
}
Future<void> test_displayName_typeParameterBound() async {
await analyze('class C<T extends num> {}');
- expect(decoratedTypeAnnotation('num').node.displayName,
+ expect(decoratedTypeAnnotation('num').node!.displayName,
'bound of C.T (test.dart:1:19)');
}
Future<void> test_displayName_typeParameterBound_implicit() async {
await analyze('class C<T extends num> {}');
expect(
- variables
+ variables!
.decoratedTypeParameterBound(
- findElement.class_('C').typeParameters[0])
- .node
+ findElement.class_('C').typeParameters[0])!
+ .node!
.displayName,
'bound of C.T (test.dart:1:19)');
}
Future<void> test_displayName_variable_local() async {
await analyze('f() { int x; }');
- expect(
- decoratedTypeAnnotation('int').node.displayName, 'f.x (test.dart:1:7)');
+ expect(decoratedTypeAnnotation('int').node!.displayName,
+ 'f.x (test.dart:1:7)');
}
Future<void> test_displayName_variable_top_level() async {
await analyze('int x;');
expect(
- decoratedTypeAnnotation('int').node.displayName, 'x (test.dart:1:1)');
+ decoratedTypeAnnotation('int').node!.displayName, 'x (test.dart:1:1)');
}
Future<void> test_dynamic_type() async {
@@ -673,7 +673,7 @@
await analyze('''
class A extends Object {}
''');
- final node = decoratedTypeAnnotation('Object').node;
+ final node = decoratedTypeAnnotation('Object').node!;
expect(node.hintActions, isEmpty);
}
@@ -684,8 +684,8 @@
}
''');
var decoratedType =
- variables.decoratedElementType(findNode.simple('x').staticElement);
- expect(decoratedType.node.isImmutable, false);
+ variables!.decoratedElementType(findNode.simple('x').staticElement!);
+ expect(decoratedType.node!.isImmutable, false);
}
Future<void> test_field_type_inferred() async {
@@ -695,7 +695,7 @@
}
''');
var decoratedType =
- variables.decoratedElementType(findNode.simple('x').staticElement);
+ variables!.decoratedElementType(findNode.simple('x').staticElement!);
expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
}
@@ -707,8 +707,8 @@
}
''');
var decoratedType =
- variables.decoratedElementType(findNode.simple('x').staticElement);
- expect(decoratedType.node.isImmutable, false);
+ variables!.decoratedElementType(findNode.simple('x').staticElement!);
+ expect(decoratedType.node!.isImmutable, false);
}
Future<void> test_field_type_simple() async {
@@ -720,8 +720,11 @@
var decoratedType = decoratedTypeAnnotation('int');
expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
expect(
- variables.decoratedElementType(
- findNode.fieldDeclaration('f').fields.variables[0].declaredElement),
+ variables!.decoratedElementType(findNode
+ .fieldDeclaration('f')
+ .fields
+ .variables[0]
+ .declaredElement!),
same(decoratedType));
}
@@ -734,11 +737,11 @@
''');
var ctor = findElement.unnamedConstructor('C');
var ctorParam = ctor.parameters[0];
- var ctorType = variables.decoratedElementType(ctor);
- var ctorParamType = variables.decoratedElementType(ctorParam);
- expect(ctorType.positionalParameters[0], same(ctorParamType));
+ var ctorType = variables!.decoratedElementType(ctor);
+ var ctorParamType = variables!.decoratedElementType(ctorParam);
+ expect(ctorType.positionalParameters![0], same(ctorParamType));
expect(ctorParamType.node, TypeMatcher<NullabilityNodeMutable>());
- expect(ctorParamType.namedParameters['i'],
+ expect(ctorParamType.namedParameters!['i'],
same(decoratedTypeAnnotation('int')));
}
@@ -752,12 +755,12 @@
''');
var ctor = findElement.unnamedConstructor('C');
var ctorParam = ctor.parameters[0];
- var ctorType = variables.decoratedElementType(ctor);
- var ctorParamType = variables.decoratedElementType(ctorParam);
- expect(ctorType.positionalParameters[0], same(ctorParamType));
+ var ctorType = variables!.decoratedElementType(ctor);
+ var ctorParamType = variables!.decoratedElementType(ctorParam);
+ expect(ctorType.positionalParameters![0], same(ctorParamType));
expect(ctorParamType.node, TypeMatcher<NullabilityNodeMutable>());
- _assertType(ctorParamType.namedParameters['i'].type, 'dynamic');
- expect(ctorParamType.namedParameters['i'].node.isImmutable, false);
+ _assertType(ctorParamType.namedParameters!['i']!.type!, 'dynamic');
+ expect(ctorParamType.namedParameters!['i']!.node!.isImmutable, false);
}
Future<void>
@@ -770,11 +773,11 @@
''');
var ctor = findElement.unnamedConstructor('C');
var ctorParam = ctor.parameters[0];
- var ctorType = variables.decoratedElementType(ctor);
- var ctorParamType = variables.decoratedElementType(ctorParam);
- expect(ctorType.positionalParameters[0], same(ctorParamType));
+ var ctorType = variables!.decoratedElementType(ctor);
+ var ctorParamType = variables!.decoratedElementType(ctorParam);
+ expect(ctorType.positionalParameters![0], same(ctorParamType));
expect(ctorParamType.node, TypeMatcher<NullabilityNodeMutable>());
- expect(ctorParamType.positionalParameters[0],
+ expect(ctorParamType.positionalParameters![0],
same(decoratedTypeAnnotation('int')));
}
@@ -788,12 +791,12 @@
''');
var ctor = findElement.unnamedConstructor('C');
var ctorParam = ctor.parameters[0];
- var ctorType = variables.decoratedElementType(ctor);
- var ctorParamType = variables.decoratedElementType(ctorParam);
- expect(ctorType.positionalParameters[0], same(ctorParamType));
+ var ctorType = variables!.decoratedElementType(ctor);
+ var ctorParamType = variables!.decoratedElementType(ctorParam);
+ expect(ctorType.positionalParameters![0], same(ctorParamType));
expect(ctorParamType.node, TypeMatcher<NullabilityNodeMutable>());
- _assertType(ctorParamType.positionalParameters[0].type, 'dynamic');
- expect(ctorParamType.positionalParameters[0].node.isImmutable, false);
+ _assertType(ctorParamType.positionalParameters![0]!.type!, 'dynamic');
+ expect(ctorParamType.positionalParameters![0]!.node!.isImmutable, false);
}
Future<void> test_fieldFormalParameter_function_return_typed() async {
@@ -805,9 +808,9 @@
''');
var ctor = findElement.unnamedConstructor('C');
var ctorParam = ctor.parameters[0];
- var ctorType = variables.decoratedElementType(ctor);
- var ctorParamType = variables.decoratedElementType(ctorParam);
- expect(ctorType.positionalParameters[0], same(ctorParamType));
+ var ctorType = variables!.decoratedElementType(ctor);
+ var ctorParamType = variables!.decoratedElementType(ctorParam);
+ expect(ctorType.positionalParameters![0], same(ctorParamType));
expect(ctorParamType.node, TypeMatcher<NullabilityNodeMutable>());
expect(ctorParamType.returnType, same(decoratedTypeAnnotation('int')));
}
@@ -821,12 +824,12 @@
''');
var ctor = findElement.unnamedConstructor('C');
var ctorParam = ctor.parameters[0];
- var ctorType = variables.decoratedElementType(ctor);
- var ctorParamType = variables.decoratedElementType(ctorParam);
- expect(ctorType.positionalParameters[0], same(ctorParamType));
+ var ctorType = variables!.decoratedElementType(ctor);
+ var ctorParamType = variables!.decoratedElementType(ctorParam);
+ expect(ctorType.positionalParameters![0], same(ctorParamType));
expect(ctorParamType.node, TypeMatcher<NullabilityNodeMutable>());
- _assertType(ctorParamType.returnType.type, 'dynamic');
- expect(ctorParamType.returnType.node.isImmutable, false);
+ _assertType(ctorParamType.returnType!.type!, 'dynamic');
+ expect(ctorParamType.returnType!.node!.isImmutable, false);
}
Future<void>
@@ -838,7 +841,7 @@
}
''');
expect(
- variables.getRequiredHint(
+ variables!.getRequiredHint(
testSource, findNode.fieldFormalParameter('this.s')),
isNotNull);
}
@@ -852,7 +855,7 @@
}
''');
expect(
- variables.getRequiredHint(
+ variables!.getRequiredHint(
testSource, findNode.fieldFormalParameter('this.s')),
isNotNull);
}
@@ -866,7 +869,7 @@
}
''');
expect(
- variables.getRequiredHint(
+ variables!.getRequiredHint(
testSource, findNode.fieldFormalParameter('this.s')),
isNotNull);
}
@@ -880,7 +883,7 @@
}
''');
expect(
- variables.getRequiredHint(
+ variables!.getRequiredHint(
testSource, findNode.fieldFormalParameter('this.s')),
isNotNull);
}
@@ -893,10 +896,10 @@
}
''');
var decoratedConstructorParamType =
- decoratedConstructorDeclaration('named').positionalParameters[0];
+ decoratedConstructorDeclaration('named').positionalParameters![0]!;
expect(decoratedTypeAnnotation('int this'),
same(decoratedConstructorParamType));
- _assertType(decoratedConstructorParamType.type, 'int');
+ _assertType(decoratedConstructorParamType.type!, 'int');
expect(decoratedConstructorParamType.node,
TypeMatcher<NullabilityNodeMutable>());
// Note: the edge builder will connect this node to the node for the type of
@@ -911,8 +914,8 @@
}
''');
var decoratedConstructorParamType =
- decoratedConstructorDeclaration('named').positionalParameters[0];
- _assertType(decoratedConstructorParamType.type, 'int');
+ decoratedConstructorDeclaration('named').positionalParameters![0]!;
+ _assertType(decoratedConstructorParamType.type!, 'int');
expect(decoratedConstructorParamType.node,
TypeMatcher<NullabilityNodeMutable>());
// Note: the edge builder will unify this implicit type with the type of the
@@ -923,7 +926,8 @@
await analyze('''
void f({/*required*/ String s}) {}
''');
- expect(variables.getRequiredHint(testSource, findNode.simpleParameter('s')),
+ expect(
+ variables!.getRequiredHint(testSource, findNode.simpleParameter('s')),
isNotNull);
}
@@ -932,7 +936,8 @@
await analyze('''
void f({@deprecated /*required*/ String s}) {}
''');
- expect(variables.getRequiredHint(testSource, findNode.simpleParameter('s')),
+ expect(
+ variables!.getRequiredHint(testSource, findNode.simpleParameter('s')),
isNotNull);
}
@@ -944,8 +949,8 @@
}
''');
expect(
- variables.getRequiredHint(
- testSource, findNode.simpleParameter('String s')),
+ variables!
+ .getRequiredHint(testSource, findNode.simpleParameter('String s')),
isNotNull);
}
@@ -954,7 +959,8 @@
await analyze('''
void f({/*required*/ final String s}) {}
''');
- expect(variables.getRequiredHint(testSource, findNode.simpleParameter('s')),
+ expect(
+ variables!.getRequiredHint(testSource, findNode.simpleParameter('s')),
isNotNull);
}
@@ -963,7 +969,8 @@
await analyze('''
void f({/*required*/ s}) {}
''');
- expect(variables.getRequiredHint(testSource, findNode.simpleParameter('s')),
+ expect(
+ variables!.getRequiredHint(testSource, findNode.simpleParameter('s')),
isNotNull);
}
@@ -971,7 +978,8 @@
await analyze('''
void f({/*required*/ var s}) {}
''');
- expect(variables.getRequiredHint(testSource, findNode.simpleParameter('s')),
+ expect(
+ variables!.getRequiredHint(testSource, findNode.simpleParameter('s')),
isNotNull);
}
@@ -983,14 +991,14 @@
''');
var decoratedType = decoratedTypeAnnotation('int');
expect(
- decoratedType.node.displayName, 'return type of C.f (test.dart:2:3)');
+ decoratedType.node!.displayName, 'return type of C.f (test.dart:2:3)');
}
Future<void> test_function_generic_bounded() async {
await analyze('''
T f<T extends Object>(T t) => t;
''');
- var bound = decoratedTypeParameterBound('T extends');
+ var bound = decoratedTypeParameterBound('T extends')!;
expect(decoratedTypeAnnotation('Object'), same(bound));
expect(bound.node, isNot(always));
expect(bound.type, typeProvider.objectType);
@@ -1000,7 +1008,7 @@
await analyze('''
T f<T>(T t) => t;
''');
- var bound = decoratedTypeParameterBound('T>');
+ var bound = decoratedTypeParameterBound('T>')!;
assertEdge(always, bound.node, hard: false);
expect(bound.type, same(typeProvider.objectType));
}
@@ -1025,14 +1033,14 @@
}
''');
var functionExpressionElement =
- findNode.simpleParameter('int i').declaredElement.enclosingElement;
+ findNode.simpleParameter('int i').declaredElement!.enclosingElement!;
var decoratedType =
- variables.decoratedElementType(functionExpressionElement);
- expect(decoratedType.positionalParameters[0],
+ variables!.decoratedElementType(functionExpressionElement);
+ expect(decoratedType.positionalParameters![0],
same(decoratedTypeAnnotation('int i')));
expect(decoratedType.node, same(never));
expect(
- decoratedType.returnType.node, TypeMatcher<NullabilityNodeMutable>());
+ decoratedType.returnType!.node, TypeMatcher<NullabilityNodeMutable>());
}
Future<void> test_functionExpression_returns_bottom() async {
@@ -1042,11 +1050,11 @@
}
''');
var functionExpressionElement =
- findNode.simpleParameter('int i').declaredElement.enclosingElement;
+ findNode.simpleParameter('int i').declaredElement!.enclosingElement!;
var decoratedType =
- variables.decoratedElementType(functionExpressionElement);
+ variables!.decoratedElementType(functionExpressionElement);
expect(
- decoratedType.returnType.node, TypeMatcher<NullabilityNodeMutable>());
+ decoratedType.returnType!.node, TypeMatcher<NullabilityNodeMutable>());
}
Future<void> test_functionTypeAlias_generic() async {
@@ -1054,7 +1062,8 @@
typedef T F<T, U>(U u);
''');
var element = findElement.typeAlias('F');
- var decoratedType = variables.decoratedElementType(element.aliasedElement);
+ var decoratedType =
+ variables!.decoratedElementType(element.aliasedElement!);
var t = element.typeParameters[0];
var u = element.typeParameters[1];
// typeFormals should be empty because this is not a generic function type,
@@ -1063,14 +1072,14 @@
expect(decoratedType.typeFormals, isEmpty);
expect(decoratedType.returnType, same(decoratedTypeAnnotation('T F')));
expect(
- (decoratedType.returnType.type as TypeParameterType).element, same(t));
+ (decoratedType.returnType!.type as TypeParameterType).element, same(t));
expect(
- decoratedType.returnType.node, TypeMatcher<NullabilityNodeMutable>());
+ decoratedType.returnType!.node, TypeMatcher<NullabilityNodeMutable>());
expect(
- (decoratedType.positionalParameters[0].type as TypeParameterType)
+ (decoratedType.positionalParameters![0]!.type as TypeParameterType)
.element,
same(u));
- expect(decoratedType.positionalParameters[0].node,
+ expect(decoratedType.positionalParameters![0]!.node,
TypeMatcher<NullabilityNodeMutable>());
}
@@ -1078,10 +1087,10 @@
await analyze('''
typedef F();
''');
- var decoratedType = variables
- .decoratedElementType(findElement.typeAlias('F').aliasedElement);
- expect(decoratedType.returnType.type.isDynamic, isTrue);
- expect(decoratedType.returnType.node.isImmutable, false);
+ var decoratedType = variables!
+ .decoratedElementType(findElement.typeAlias('F').aliasedElement!);
+ expect(decoratedType.returnType!.type!.isDynamic, isTrue);
+ expect(decoratedType.returnType!.node!.isImmutable, false);
expect(decoratedType.typeFormals, isEmpty);
}
@@ -1089,11 +1098,11 @@
await analyze('''
typedef int F(String s);
''');
- var decoratedType = variables
- .decoratedElementType(findElement.typeAlias('F').aliasedElement);
+ var decoratedType = variables!
+ .decoratedElementType(findElement.typeAlias('F').aliasedElement!);
expect(decoratedType.returnType, same(decoratedTypeAnnotation('int')));
expect(decoratedType.typeFormals, isEmpty);
- expect(decoratedType.positionalParameters[0],
+ expect(decoratedType.positionalParameters![0],
same(decoratedTypeAnnotation('String')));
}
@@ -1103,7 +1112,7 @@
void f({/*required*/ void s()}) {}
''');
expect(
- variables.getRequiredHint(
+ variables!.getRequiredHint(
testSource, findNode.functionTypedFormalParameter('s')),
isNotNull);
}
@@ -1114,7 +1123,7 @@
void f({/*required*/ s()}) {}
''');
expect(
- variables.getRequiredHint(
+ variables!.getRequiredHint(
testSource, findNode.functionTypedFormalParameter('s')),
isNotNull);
}
@@ -1125,11 +1134,11 @@
''');
var f = findElement.function('f');
var g = f.parameters[0];
- var fType = variables.decoratedElementType(f);
- var gType = variables.decoratedElementType(g);
- expect(fType.positionalParameters[0], same(gType));
+ var fType = variables!.decoratedElementType(f);
+ var gType = variables!.decoratedElementType(g);
+ expect(fType.positionalParameters![0], same(gType));
expect(gType.node, TypeMatcher<NullabilityNodeMutable>());
- expect(gType.namedParameters['i'], same(decoratedTypeAnnotation('int')));
+ expect(gType.namedParameters!['i'], same(decoratedTypeAnnotation('int')));
}
Future<void>
@@ -1139,12 +1148,12 @@
''');
var f = findElement.function('f');
var g = f.parameters[0];
- var fType = variables.decoratedElementType(f);
- var gType = variables.decoratedElementType(g);
- expect(fType.positionalParameters[0], same(gType));
+ var fType = variables!.decoratedElementType(f);
+ var gType = variables!.decoratedElementType(g);
+ expect(fType.positionalParameters![0], same(gType));
expect(gType.node, TypeMatcher<NullabilityNodeMutable>());
- _assertType(gType.namedParameters['i'].type, 'dynamic');
- expect(gType.namedParameters['i'].node.isImmutable, false);
+ _assertType(gType.namedParameters!['i']!.type!, 'dynamic');
+ expect(gType.namedParameters!['i']!.node!.isImmutable, false);
}
Future<void>
@@ -1154,11 +1163,12 @@
''');
var f = findElement.function('f');
var g = f.parameters[0];
- var fType = variables.decoratedElementType(f);
- var gType = variables.decoratedElementType(g);
- expect(fType.positionalParameters[0], same(gType));
+ var fType = variables!.decoratedElementType(f);
+ var gType = variables!.decoratedElementType(g);
+ expect(fType.positionalParameters![0], same(gType));
expect(gType.node, TypeMatcher<NullabilityNodeMutable>());
- expect(gType.positionalParameters[0], same(decoratedTypeAnnotation('int')));
+ expect(
+ gType.positionalParameters![0], same(decoratedTypeAnnotation('int')));
}
Future<void>
@@ -1168,12 +1178,12 @@
''');
var f = findElement.function('f');
var g = f.parameters[0];
- var fType = variables.decoratedElementType(f);
- var gType = variables.decoratedElementType(g);
- expect(fType.positionalParameters[0], same(gType));
+ var fType = variables!.decoratedElementType(f);
+ var gType = variables!.decoratedElementType(g);
+ expect(fType.positionalParameters![0], same(gType));
expect(gType.node, TypeMatcher<NullabilityNodeMutable>());
- _assertType(gType.positionalParameters[0].type, 'dynamic');
- expect(gType.positionalParameters[0].node.isImmutable, false);
+ _assertType(gType.positionalParameters![0]!.type!, 'dynamic');
+ expect(gType.positionalParameters![0]!.node!.isImmutable, false);
}
Future<void> test_functionTypedFormalParameter_return_typed() async {
@@ -1182,9 +1192,9 @@
''');
var f = findElement.function('f');
var g = f.parameters[0];
- var fType = variables.decoratedElementType(f);
- var gType = variables.decoratedElementType(g);
- expect(fType.positionalParameters[0], same(gType));
+ var fType = variables!.decoratedElementType(f);
+ var gType = variables!.decoratedElementType(g);
+ expect(fType.positionalParameters![0], same(gType));
expect(gType.node, TypeMatcher<NullabilityNodeMutable>());
expect(gType.returnType, same(decoratedTypeAnnotation('int')));
}
@@ -1195,12 +1205,12 @@
''');
var f = findElement.function('f');
var g = f.parameters[0];
- var fType = variables.decoratedElementType(f);
- var gType = variables.decoratedElementType(g);
- expect(fType.positionalParameters[0], same(gType));
+ var fType = variables!.decoratedElementType(f);
+ var gType = variables!.decoratedElementType(g);
+ expect(fType.positionalParameters![0], same(gType));
expect(gType.node, TypeMatcher<NullabilityNodeMutable>());
- _assertType(gType.returnType.type, 'dynamic');
- expect(gType.returnType.node.isImmutable, false);
+ _assertType(gType.returnType!.type!, 'dynamic');
+ expect(gType.returnType!.node!.isImmutable, false);
}
Future<void> test_genericFunctionType_formals() async {
@@ -1208,17 +1218,17 @@
void f(T Function<T, U>(U) x) {}
''');
var decoratedType = decoratedGenericFunctionTypeAnnotation('T Function');
- expect(decoratedFunctionType('f').positionalParameters[0],
+ expect(decoratedFunctionType('f').positionalParameters![0],
same(decoratedType));
expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
- _assertType(decoratedType.type, 'T Function<T, U>(U)');
+ _assertType(decoratedType.type!, 'T Function<T, U>(U)');
expect(decoratedType.typeFormals, hasLength(2));
- var t = decoratedType.typeFormals[0];
- var u = decoratedType.typeFormals[1];
+ var t = decoratedType.typeFormals![0];
+ var u = decoratedType.typeFormals![1];
expect(
- (decoratedType.returnType.type as TypeParameterType).element, same(t));
+ (decoratedType.returnType!.type as TypeParameterType).element, same(t));
expect(
- (decoratedType.positionalParameters[0].type as TypeParameterType)
+ (decoratedType.positionalParameters![0]!.type as TypeParameterType)
.element,
same(u));
}
@@ -1229,11 +1239,11 @@
''');
var decoratedType =
decoratedGenericFunctionTypeAnnotation('void Function({int y})');
- expect(decoratedFunctionType('f').positionalParameters[0],
+ expect(decoratedFunctionType('f').positionalParameters![0],
same(decoratedType));
expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
var decoratedIntType = decoratedTypeAnnotation('int');
- expect(decoratedType.namedParameters['y'], same(decoratedIntType));
+ expect(decoratedType.namedParameters!['y'], same(decoratedIntType));
expect(decoratedIntType.node, isNotNull);
expect(decoratedIntType.node, isNot(never));
}
@@ -1244,14 +1254,14 @@
''');
var decoratedType =
decoratedGenericFunctionTypeAnnotation('int Function()');
- expect(decoratedFunctionType('f').positionalParameters[0],
+ expect(decoratedFunctionType('f').positionalParameters![0],
same(decoratedType));
expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
var decoratedIntType = decoratedTypeAnnotation('int');
expect(decoratedType.returnType, same(decoratedIntType));
expect(decoratedIntType.node, isNotNull);
expect(decoratedIntType.node, isNot(never));
- expect(decoratedType.returnType.node.displayName,
+ expect(decoratedType.returnType!.node!.displayName,
'return type of parameter 0 of f (test.dart:1:8)');
}
@@ -1262,10 +1272,10 @@
}
''');
var decoratedFType = decoratedMethodType('f');
- var decoratedFReturnType = decoratedFType.returnType;
- var decoratedFReturnReturnType = decoratedFReturnType.returnType;
- _assertType(decoratedFReturnReturnType.type, 'dynamic');
- expect(decoratedFReturnReturnType.node.isImmutable, false);
+ var decoratedFReturnType = decoratedFType.returnType!;
+ var decoratedFReturnReturnType = decoratedFReturnType.returnType!;
+ _assertType(decoratedFReturnReturnType.type!, 'dynamic');
+ expect(decoratedFReturnReturnType.node!.isImmutable, false);
}
Future<void> test_genericFunctionType_unnamedParameterType() async {
@@ -1274,11 +1284,11 @@
''');
var decoratedType =
decoratedGenericFunctionTypeAnnotation('void Function(int)');
- expect(decoratedFunctionType('f').positionalParameters[0],
+ expect(decoratedFunctionType('f').positionalParameters![0],
same(decoratedType));
expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
var decoratedIntType = decoratedTypeAnnotation('int');
- expect(decoratedType.positionalParameters[0], same(decoratedIntType));
+ expect(decoratedType.positionalParameters![0], same(decoratedIntType));
expect(decoratedIntType.node, isNotNull);
expect(decoratedIntType.node, isNot(never));
}
@@ -1288,22 +1298,23 @@
typedef F = T Function<T, U>(U u);
''');
var element = findElement.typeAlias('F');
- var decoratedType = variables.decoratedElementType(element.aliasedElement);
+ var decoratedType =
+ variables!.decoratedElementType(element.aliasedElement!);
expect(decoratedType,
same(decoratedGenericFunctionTypeAnnotation('T Function')));
expect(decoratedType.typeFormals, hasLength(2));
- var t = decoratedType.typeFormals[0];
- var u = decoratedType.typeFormals[1];
+ var t = decoratedType.typeFormals![0];
+ var u = decoratedType.typeFormals![1];
expect(decoratedType.returnType, same(decoratedTypeAnnotation('T F')));
expect(
- (decoratedType.returnType.type as TypeParameterType).element, same(t));
+ (decoratedType.returnType!.type as TypeParameterType).element, same(t));
expect(
- decoratedType.returnType.node, TypeMatcher<NullabilityNodeMutable>());
+ decoratedType.returnType!.node, TypeMatcher<NullabilityNodeMutable>());
expect(
- (decoratedType.positionalParameters[0].type as TypeParameterType)
+ (decoratedType.positionalParameters![0]!.type as TypeParameterType)
.element,
same(u));
- expect(decoratedType.positionalParameters[0].node,
+ expect(decoratedType.positionalParameters![0]!.node,
TypeMatcher<NullabilityNodeMutable>());
}
@@ -1312,7 +1323,8 @@
typedef F<T, U> = T Function(U u);
''');
var element = findElement.typeAlias('F');
- var decoratedType = variables.decoratedElementType(element.aliasedElement);
+ var decoratedType =
+ variables!.decoratedElementType(element.aliasedElement!);
expect(decoratedType,
same(decoratedGenericFunctionTypeAnnotation('T Function')));
var t = element.typeParameters[0];
@@ -1323,14 +1335,14 @@
expect(decoratedType.typeFormals, isEmpty);
expect(decoratedType.returnType, same(decoratedTypeAnnotation('T F')));
expect(
- (decoratedType.returnType.type as TypeParameterType).element, same(t));
+ (decoratedType.returnType!.type as TypeParameterType).element, same(t));
expect(
- decoratedType.returnType.node, TypeMatcher<NullabilityNodeMutable>());
+ decoratedType.returnType!.node, TypeMatcher<NullabilityNodeMutable>());
expect(
- (decoratedType.positionalParameters[0].type as TypeParameterType)
+ (decoratedType.positionalParameters![0]!.type as TypeParameterType)
.element,
same(u));
- expect(decoratedType.positionalParameters[0].node,
+ expect(decoratedType.positionalParameters![0]!.node,
TypeMatcher<NullabilityNodeMutable>());
}
@@ -1338,12 +1350,12 @@
await analyze('''
typedef F = Function();
''');
- var decoratedType = variables
- .decoratedElementType(findElement.typeAlias('F').aliasedElement);
+ var decoratedType = variables!
+ .decoratedElementType(findElement.typeAlias('F').aliasedElement!);
expect(decoratedType,
same(decoratedGenericFunctionTypeAnnotation('Function')));
- expect(decoratedType.returnType.type.isDynamic, isTrue);
- expect(decoratedType.returnType.node.isImmutable, false);
+ expect(decoratedType.returnType!.type!.isDynamic, isTrue);
+ expect(decoratedType.returnType!.node!.isImmutable, false);
expect(decoratedType.typeFormals, isEmpty);
}
@@ -1351,15 +1363,15 @@
await analyze('''
typedef F = int Function(String s);
''');
- var decoratedType = variables
- .decoratedElementType(findElement.typeAlias('F').aliasedElement);
+ var decoratedType = variables!
+ .decoratedElementType(findElement.typeAlias('F').aliasedElement!);
expect(decoratedType,
same(decoratedGenericFunctionTypeAnnotation('int Function')));
expect(decoratedType.returnType, same(decoratedTypeAnnotation('int')));
expect(decoratedType.typeFormals, isEmpty);
- expect(decoratedType.positionalParameters[0],
+ expect(decoratedType.positionalParameters![0],
same(decoratedTypeAnnotation('String')));
- expect(decoratedType.returnType.node.displayName,
+ expect(decoratedType.returnType!.node!.displayName,
'return type of F (test.dart:1:13)');
}
@@ -1367,14 +1379,14 @@
await analyze('''
var x = [1];
''');
- final node = variables
+ final node = variables!
.decoratedElementType(findNode
.topLevelVariableDeclaration('x')
.variables
.variables[0]
- .declaredElement)
- .typeArguments[0]
- .node;
+ .declaredElement!)
+ .typeArguments[0]!
+ .node!;
expect(node.hintActions, isEmpty);
}
@@ -1382,13 +1394,13 @@
await analyze('''
var x = 1;
''');
- final node = variables
+ final node = variables!
.decoratedElementType(findNode
.topLevelVariableDeclaration('x')
.variables
.variables[0]
- .declaredElement)
- .node;
+ .declaredElement!)
+ .node!;
expect(node.hintActions, isEmpty);
}
@@ -1397,12 +1409,12 @@
void f(List x) {}
''');
var decoratedListType = decoratedTypeAnnotation('List');
- expect(decoratedFunctionType('f').positionalParameters[0],
+ expect(decoratedFunctionType('f').positionalParameters![0],
same(decoratedListType));
expect(decoratedListType.node, isNotNull);
expect(decoratedListType.node, isNot(never));
- var decoratedArgType = decoratedListType.typeArguments[0];
- expect(decoratedArgType.node.isImmutable, false);
+ var decoratedArgType = decoratedListType.typeArguments[0]!;
+ expect(decoratedArgType.node!.isImmutable, false);
}
Future<void> test_interfaceType_generic_instantiate_to_function_type() async {
@@ -1411,14 +1423,14 @@
void f(C x) {}
''');
var decoratedCType = decoratedTypeAnnotation('C x');
- expect(decoratedFunctionType('f').positionalParameters[0],
+ expect(decoratedFunctionType('f').positionalParameters![0],
same(decoratedCType));
expect(decoratedCType.node, TypeMatcher<NullabilityNodeMutable>());
expect(decoratedCType.typeArguments, hasLength(1));
- var decoratedArgType = decoratedCType.typeArguments[0];
+ var decoratedArgType = decoratedCType.typeArguments[0]!;
expect(decoratedArgType.node, TypeMatcher<NullabilityNodeMutable>());
expect(decoratedArgType.typeArguments, isEmpty);
- var decoratedArgReturnType = decoratedArgType.returnType;
+ var decoratedArgReturnType = decoratedArgType.returnType!;
expect(decoratedArgReturnType.node, TypeMatcher<NullabilityNodeMutable>());
expect(decoratedArgReturnType.typeArguments, isEmpty);
}
@@ -1430,15 +1442,15 @@
void f(C x) {}
''');
var decoratedCType = decoratedTypeAnnotation('C x');
- expect(decoratedFunctionType('f').positionalParameters[0],
+ expect(decoratedFunctionType('f').positionalParameters![0],
same(decoratedCType));
expect(decoratedCType.node, TypeMatcher<NullabilityNodeMutable>());
expect(decoratedCType.typeArguments, hasLength(1));
- var decoratedArgType = decoratedCType.typeArguments[0];
+ var decoratedArgType = decoratedCType.typeArguments[0]!;
expect(decoratedArgType.node, TypeMatcher<NullabilityNodeMutable>());
expect(decoratedArgType.typeArguments, isEmpty);
- var decoratedArgReturnType = decoratedArgType.returnType;
- expect(decoratedArgReturnType.node.isImmutable, false);
+ var decoratedArgReturnType = decoratedArgType.returnType!;
+ expect(decoratedArgReturnType.node!.isImmutable, false);
expect(decoratedArgReturnType.typeArguments, isEmpty);
}
@@ -1449,14 +1461,14 @@
void f(D x) {}
''');
var decoratedDType = decoratedTypeAnnotation('D x');
- expect(decoratedFunctionType('f').positionalParameters[0],
+ expect(decoratedFunctionType('f').positionalParameters![0],
same(decoratedDType));
expect(decoratedDType.node, TypeMatcher<NullabilityNodeMutable>());
expect(decoratedDType.typeArguments, hasLength(1));
- var decoratedArgType = decoratedDType.typeArguments[0];
+ var decoratedArgType = decoratedDType.typeArguments[0]!;
expect(decoratedArgType.node, TypeMatcher<NullabilityNodeMutable>());
expect(decoratedArgType.typeArguments, hasLength(1));
- var decoratedArgArgType = decoratedArgType.typeArguments[0];
+ var decoratedArgArgType = decoratedArgType.typeArguments[0]!;
expect(decoratedArgArgType.node, TypeMatcher<NullabilityNodeMutable>());
expect(decoratedArgArgType.typeArguments, isEmpty);
}
@@ -1469,26 +1481,26 @@
void f(D x) {}
''');
var decoratedDType = decoratedTypeAnnotation('D x');
- expect(decoratedFunctionType('f').positionalParameters[0],
+ expect(decoratedFunctionType('f').positionalParameters![0],
same(decoratedDType));
expect(decoratedDType.node, TypeMatcher<NullabilityNodeMutable>());
expect(decoratedDType.typeArguments, hasLength(2));
- var decoratedArg0Type = decoratedDType.typeArguments[0];
+ var decoratedArg0Type = decoratedDType.typeArguments[0]!;
expect(decoratedArg0Type.node, TypeMatcher<NullabilityNodeMutable>());
expect(decoratedArg0Type.typeArguments, hasLength(2));
- var decoratedArg0Arg0Type = decoratedArg0Type.typeArguments[0];
+ var decoratedArg0Arg0Type = decoratedArg0Type.typeArguments[0]!;
expect(decoratedArg0Arg0Type.node, TypeMatcher<NullabilityNodeMutable>());
expect(decoratedArg0Arg0Type.typeArguments, isEmpty);
- var decoratedArg0Arg1Type = decoratedArg0Type.typeArguments[1];
+ var decoratedArg0Arg1Type = decoratedArg0Type.typeArguments[1]!;
expect(decoratedArg0Arg1Type.node, TypeMatcher<NullabilityNodeMutable>());
expect(decoratedArg0Arg1Type.typeArguments, isEmpty);
- var decoratedArg1Type = decoratedDType.typeArguments[1];
+ var decoratedArg1Type = decoratedDType.typeArguments[1]!;
expect(decoratedArg1Type.node, TypeMatcher<NullabilityNodeMutable>());
expect(decoratedArg1Type.typeArguments, hasLength(2));
- var decoratedArg1Arg0Type = decoratedArg1Type.typeArguments[0];
+ var decoratedArg1Arg0Type = decoratedArg1Type.typeArguments[0]!;
expect(decoratedArg1Arg0Type.node, TypeMatcher<NullabilityNodeMutable>());
expect(decoratedArg1Arg0Type.typeArguments, isEmpty);
- var decoratedArg1Arg1Type = decoratedArg1Type.typeArguments[1];
+ var decoratedArg1Arg1Type = decoratedArg1Type.typeArguments[1]!;
expect(decoratedArg1Arg1Type.node, TypeMatcher<NullabilityNodeMutable>());
expect(decoratedArg1Arg1Type.typeArguments, isEmpty);
}
@@ -1499,11 +1511,11 @@
void f(C x) {}
''');
var decoratedListType = decoratedTypeAnnotation('C x');
- expect(decoratedFunctionType('f').positionalParameters[0],
+ expect(decoratedFunctionType('f').positionalParameters![0],
same(decoratedListType));
expect(decoratedListType.node, TypeMatcher<NullabilityNodeMutable>());
expect(decoratedListType.typeArguments, hasLength(1));
- var decoratedArgType = decoratedListType.typeArguments[0];
+ var decoratedArgType = decoratedListType.typeArguments[0]!;
expect(decoratedArgType.node, TypeMatcher<NullabilityNodeMutable>());
expect(decoratedArgType.typeArguments, isEmpty);
}
@@ -1513,7 +1525,7 @@
void f(List<int> x) {}
''');
var decoratedListType = decoratedTypeAnnotation('List<int>');
- expect(decoratedFunctionType('f').positionalParameters[0],
+ expect(decoratedFunctionType('f').positionalParameters![0],
same(decoratedListType));
expect(decoratedListType.node, isNotNull);
expect(decoratedListType.node, isNot(never));
@@ -1531,7 +1543,7 @@
''');
var decoratedType = decoratedFunctionType('g');
expect(decoratedType.returnType, same(decoratedTypeAnnotation('int g')));
- expect(decoratedType.positionalParameters[0],
+ expect(decoratedType.positionalParameters![0],
same(decoratedTypeAnnotation('int i')));
expect(decoratedType.node, same(never));
}
@@ -1543,8 +1555,8 @@
}
''');
var decoratedType =
- variables.decoratedElementType(findNode.simple('x').staticElement);
- expect(decoratedType.node.isImmutable, false);
+ variables!.decoratedElementType(findNode.simple('x').staticElement!);
+ expect(decoratedType.node!.isImmutable, false);
}
Future<void> test_localVariable_type_inferred() async {
@@ -1554,7 +1566,7 @@
}
''');
var decoratedType =
- variables.decoratedElementType(findNode.simple('x').staticElement);
+ variables!.decoratedElementType(findNode.simple('x').staticElement!);
expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
}
@@ -1566,8 +1578,8 @@
}
''');
var decoratedType =
- variables.decoratedElementType(findNode.simple('x').staticElement);
- expect(decoratedType.node.isImmutable, false);
+ variables!.decoratedElementType(findNode.simple('x').staticElement!);
+ expect(decoratedType.node!.isImmutable, false);
}
Future<void> test_localVariable_type_inferred_function() async {
@@ -1577,8 +1589,8 @@
}
''');
var decoratedType =
- variables.decoratedElementType(findNode.simple('x').staticElement);
- expect(decoratedType.returnType.node.displayName,
+ variables!.decoratedElementType(findNode.simple('x').staticElement!);
+ expect(decoratedType.returnType!.node!.displayName,
'return type of main.x (test.dart:2:7)');
}
@@ -1589,10 +1601,10 @@
}
''');
var decoratedType =
- variables.decoratedElementType(findNode.simple('x').staticElement);
- expect(decoratedType.typeArguments[0].node.displayName,
+ variables!.decoratedElementType(findNode.simple('x').staticElement!);
+ expect(decoratedType.typeArguments[0]!.node!.displayName,
'type argument 0 of main.x (test.dart:2:7)');
- expect(decoratedType.typeArguments[1].node.displayName,
+ expect(decoratedType.typeArguments[1]!.node!.displayName,
'type argument 1 of main.x (test.dart:2:7)');
}
@@ -1602,7 +1614,7 @@
T f<T extends Object>(T t) => t;
}
''');
- var bound = decoratedTypeParameterBound('T extends');
+ var bound = decoratedTypeParameterBound('T extends')!;
expect(decoratedTypeAnnotation('Object'), same(bound));
expect(bound.node, isNot(always));
expect(bound.type, typeProvider.objectType);
@@ -1614,7 +1626,7 @@
T f<T>(T t) => t;
}
''');
- var bound = decoratedTypeParameterBound('T>');
+ var bound = decoratedTypeParameterBound('T>')!;
assertEdge(always, bound.node, hard: false);
expect(bound.type, same(typeProvider.objectType));
}
@@ -1640,8 +1652,8 @@
void f(x) {}
}
''');
- var decoratedType = decoratedMethodType('f').positionalParameters[0];
- expect(decoratedType.node.isImmutable, false);
+ var decoratedType = decoratedMethodType('f').positionalParameters![0]!;
+ expect(decoratedType.node!.isImmutable, false);
}
Future<void> test_method_parameterType_implicit_dynamic_named() async {
@@ -1650,8 +1662,8 @@
void f({x}) {}
}
''');
- var decoratedType = decoratedMethodType('f').namedParameters['x'];
- expect(decoratedType.node.isImmutable, false);
+ var decoratedType = decoratedMethodType('f').namedParameters!['x']!;
+ expect(decoratedType.node!.isImmutable, false);
}
Future<void> test_method_parameterType_inferred() async {
@@ -1663,7 +1675,7 @@
void f/*C*/(x) {}
}
''');
- var decoratedType = decoratedMethodType('f/*C*/').positionalParameters[0];
+ var decoratedType = decoratedMethodType('f/*C*/').positionalParameters![0]!;
expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
}
@@ -1676,8 +1688,8 @@
void f/*C*/(x) {}
}
''');
- var decoratedType = decoratedMethodType('f/*C*/').positionalParameters[0];
- expect(decoratedType.node.isImmutable, false);
+ var decoratedType = decoratedMethodType('f/*C*/').positionalParameters![0]!;
+ expect(decoratedType.node!.isImmutable, false);
}
Future<void> test_method_parameterType_inferred_dynamic_named() async {
@@ -1689,8 +1701,8 @@
void f/*C*/({x = 0}) {}
}
''');
- var decoratedType = decoratedMethodType('f/*C*/').namedParameters['x'];
- expect(decoratedType.node.isImmutable, false);
+ var decoratedType = decoratedMethodType('f/*C*/').namedParameters!['x']!;
+ expect(decoratedType.node!.isImmutable, false);
}
Future<void>
@@ -1704,13 +1716,13 @@
}
''');
var decoratedBaseType =
- decoratedMethodType('f/*B*/').positionalParameters[0];
- var decoratedType = decoratedMethodType('f/*C*/').positionalParameters[0];
+ decoratedMethodType('f/*B*/').positionalParameters![0]!;
+ var decoratedType = decoratedMethodType('f/*C*/').positionalParameters![0]!;
var decoratedTypeFormalBound = decoratedTypeParameterBounds
- .get((decoratedType.type as FunctionType).typeFormals[0]);
- _assertType(decoratedTypeFormalBound.type, 'Object');
+ .get((decoratedType.type as FunctionType).typeFormals[0])!;
+ _assertType(decoratedTypeFormalBound.type!, 'Object');
var decoratedBaseTypeFormalBound = decoratedTypeParameterBounds
- .get((decoratedBaseType.type as FunctionType).typeFormals[0]);
+ .get((decoratedBaseType.type as FunctionType).typeFormals[0])!;
expect(decoratedTypeFormalBound.node,
isNot(same(decoratedBaseTypeFormalBound.node)));
}
@@ -1726,13 +1738,13 @@
}
''');
var decoratedBaseType =
- decoratedMethodType('f/*B*/').positionalParameters[0];
- var decoratedType = decoratedMethodType('f/*C*/').positionalParameters[0];
+ decoratedMethodType('f/*B*/').positionalParameters![0]!;
+ var decoratedType = decoratedMethodType('f/*C*/').positionalParameters![0]!;
var decoratedTypeFormalBound = decoratedTypeParameterBounds
- .get((decoratedType.type as FunctionType).typeFormals[0]);
- _assertType(decoratedTypeFormalBound.type, 'num');
+ .get((decoratedType.type as FunctionType).typeFormals[0])!;
+ _assertType(decoratedTypeFormalBound.type!, 'num');
var decoratedBaseTypeFormalBound = decoratedTypeParameterBounds
- .get((decoratedBaseType.type as FunctionType).typeFormals[0]);
+ .get((decoratedBaseType.type as FunctionType).typeFormals[0])!;
expect(decoratedTypeFormalBound.node,
isNot(same(decoratedBaseTypeFormalBound.node)));
}
@@ -1746,7 +1758,7 @@
void f/*C*/({x = 0}) {}
}
''');
- var decoratedType = decoratedMethodType('f/*C*/').namedParameters['x'];
+ var decoratedType = decoratedMethodType('f/*C*/').namedParameters!['x']!;
expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
}
@@ -1756,8 +1768,8 @@
f() => 1;
}
''');
- var decoratedType = decoratedMethodType('f').returnType;
- expect(decoratedType.node.isImmutable, false);
+ var decoratedType = decoratedMethodType('f').returnType!;
+ expect(decoratedType.node!.isImmutable, false);
}
Future<void> test_method_returnType_inferred() async {
@@ -1769,7 +1781,7 @@
f/*C*/() => 1;
}
''');
- var decoratedType = decoratedMethodType('f/*C*/').returnType;
+ var decoratedType = decoratedMethodType('f/*C*/').returnType!;
expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
}
@@ -1782,8 +1794,8 @@
f/*C*/() => 1;
}
''');
- var decoratedType = decoratedMethodType('f/*C*/').returnType;
- expect(decoratedType.node.isImmutable, false);
+ var decoratedType = decoratedMethodType('f/*C*/').returnType!;
+ expect(decoratedType.node!.isImmutable, false);
}
Future<void> test_parameters() async {
@@ -1800,10 +1812,10 @@
void f(x) {}
''');
var decoratedType =
- variables.decoratedElementType(findNode.simple('x').staticElement);
- expect(decoratedFunctionType('f').positionalParameters[0],
+ variables!.decoratedElementType(findNode.simple('x').staticElement!);
+ expect(decoratedFunctionType('f').positionalParameters![0],
same(decoratedType));
- expect(decoratedType.type.isDynamic, isTrue);
+ expect(decoratedType.type!.isDynamic, isTrue);
}
Future<void> test_topLevelFunction_parameterType_named_no_default() async {
@@ -1812,11 +1824,11 @@
''');
var decoratedType = decoratedTypeAnnotation('String');
var functionType = decoratedFunctionType('f');
- expect(functionType.namedParameters['s'], same(decoratedType));
+ expect(functionType.namedParameters!['s'], same(decoratedType));
expect(decoratedType.node, isNotNull);
expect(decoratedType.node, isNot(never));
expect(decoratedType.node, isNot(always));
- expect(functionType.namedParameters['s'].node.isPossiblyOptional, true);
+ expect(functionType.namedParameters!['s']!.node!.isPossiblyOptional, true);
}
Future<void>
@@ -1828,11 +1840,11 @@
''');
var decoratedType = decoratedTypeAnnotation('String');
var functionType = decoratedFunctionType('f');
- expect(functionType.namedParameters['s'], same(decoratedType));
+ expect(functionType.namedParameters!['s'], same(decoratedType));
expect(decoratedType.node, isNotNull);
expect(decoratedType.node, isNot(never));
expect(decoratedType.node, isNot(always));
- expect(functionType.namedParameters['s'].node.isPossiblyOptional, false);
+ expect(functionType.namedParameters!['s']!.node!.isPossiblyOptional, false);
}
Future<void>
@@ -1844,12 +1856,13 @@
''');
var decoratedType = decoratedTypeAnnotation('String');
var functionType = decoratedFunctionType('f');
- expect(functionType.namedParameters['s'], same(decoratedType));
+ expect(functionType.namedParameters!['s'], same(decoratedType));
expect(decoratedType.node, isNotNull);
expect(decoratedType.node, isNot(never));
expect(decoratedType.node, isNot(always));
- expect(functionType.namedParameters['s'].node.isPossiblyOptional, false);
- expect(variables.getRequiredHint(testSource, findNode.simpleParameter('s')),
+ expect(functionType.namedParameters!['s']!.node!.isPossiblyOptional, false);
+ expect(
+ variables!.getRequiredHint(testSource, findNode.simpleParameter('s')),
isNotNull);
}
@@ -1859,10 +1872,10 @@
''');
var decoratedType = decoratedTypeAnnotation('String');
var functionType = decoratedFunctionType('f');
- expect(functionType.namedParameters['s'], same(decoratedType));
+ expect(functionType.namedParameters!['s'], same(decoratedType));
expect(decoratedType.node, isNotNull);
expect(decoratedType.node, isNot(never));
- expect(functionType.namedParameters['s'].node.isPossiblyOptional, false);
+ expect(functionType.namedParameters!['s']!.node!.isPossiblyOptional, false);
}
Future<void> test_topLevelFunction_parameterType_positionalOptional() async {
@@ -1870,7 +1883,7 @@
void f([int i]) {}
''');
var decoratedType = decoratedTypeAnnotation('int');
- expect(decoratedFunctionType('f').positionalParameters[0],
+ expect(decoratedFunctionType('f').positionalParameters![0],
same(decoratedType));
expect(decoratedType.node, isNotNull);
expect(decoratedType.node, isNot(never));
@@ -1881,7 +1894,7 @@
void f(int i) {}
''');
var decoratedType = decoratedTypeAnnotation('int');
- expect(decoratedFunctionType('f').positionalParameters[0],
+ expect(decoratedFunctionType('f').positionalParameters![0],
same(decoratedType));
expect(decoratedType.node, isNotNull);
expect(decoratedType.node, isNot(never));
@@ -1891,8 +1904,8 @@
await analyze('''
f() {}
''');
- var decoratedType = decoratedFunctionType('f').returnType;
- expect(decoratedType.type.isDynamic, isTrue);
+ var decoratedType = decoratedFunctionType('f').returnType!;
+ expect(decoratedType.type!.isDynamic, isTrue);
}
Future<void> test_topLevelFunction_returnType_simple() async {
@@ -1910,8 +1923,8 @@
var x;
''');
var decoratedType =
- variables.decoratedElementType(findNode.simple('x').staticElement);
- expect(decoratedType.node.isImmutable, false);
+ variables!.decoratedElementType(findNode.simple('x').staticElement!);
+ expect(decoratedType.node!.isImmutable, false);
}
Future<void> test_topLevelVariable_type_inferred() async {
@@ -1919,7 +1932,7 @@
var x = 1;
''');
var decoratedType =
- variables.decoratedElementType(findNode.simple('x').staticElement);
+ variables!.decoratedElementType(findNode.simple('x').staticElement!);
expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
}
@@ -1929,19 +1942,19 @@
var x = f();
''');
var decoratedType =
- variables.decoratedElementType(findNode.simple('x').staticElement);
- expect(decoratedType.node.isImmutable, false);
+ variables!.decoratedElementType(findNode.simple('x').staticElement!);
+ expect(decoratedType.node!.isImmutable, false);
}
Future<void> test_type_add_non_null_hint() async {
await analyze('''
void f(int i) {}
''');
- final node = decoratedTypeAnnotation('int').node;
+ final node = decoratedTypeAnnotation('int').node!;
expect(node.hintActions, contains(HintActionKind.addNonNullableHint));
expect(
node.hintActions[HintActionKind.addNonNullableHint]
- .applyTo(super.testCode),
+ .applyTo(super.testCode!),
'''
void f(int/*!*/ i) {}
''');
@@ -1951,11 +1964,11 @@
await analyze('''
void f(int i) {}
''');
- final node = decoratedTypeAnnotation('int').node;
+ final node = decoratedTypeAnnotation('int').node!;
expect(node.hintActions, contains(HintActionKind.addNullableHint));
expect(
node.hintActions[HintActionKind.addNullableHint]
- .applyTo(super.testCode),
+ .applyTo(super.testCode!),
'''
void f(int/*?*/ i) {}
''');
@@ -1965,7 +1978,7 @@
await analyze('''
void f(int/*!*/ i) {}
''');
- final node = decoratedTypeAnnotation('int').node;
+ final node = decoratedTypeAnnotation('int').node!;
assertEdge(node, never, hard: true, checkable: false);
expect(
node.hintActions, isNot(contains(HintActionKind.addNonNullableHint)));
@@ -1974,13 +1987,13 @@
isNot(contains(HintActionKind.changeToNonNullableHint)));
expect(
node.hintActions[HintActionKind.removeNonNullableHint]
- .applyTo(super.testCode),
+ .applyTo(super.testCode!),
'''
void f(int i) {}
''');
expect(
node.hintActions[HintActionKind.changeToNullableHint]
- .applyTo(super.testCode),
+ .applyTo(super.testCode!),
'''
void f(int/*?*/ i) {}
''');
@@ -1990,7 +2003,7 @@
await analyze('''
void f(int/*?*/ i) {}
''');
- final node = decoratedTypeAnnotation('int').node;
+ final node = decoratedTypeAnnotation('int').node!;
assertUnion(always, node);
expect(
node.hintActions, isNot(contains(HintActionKind.addNonNullableHint)));
@@ -1999,13 +2012,13 @@
node.hintActions, isNot(contains(HintActionKind.changeToNullableHint)));
expect(
node.hintActions[HintActionKind.removeNullableHint]
- .applyTo(super.testCode),
+ .applyTo(super.testCode!),
'''
void f(int i) {}
''');
expect(
node.hintActions[HintActionKind.changeToNonNullableHint]
- .applyTo(super.testCode),
+ .applyTo(super.testCode!),
'''
void f(int/*!*/ i) {}
''');
@@ -2015,11 +2028,11 @@
await analyze('''
void f(List<int> i) {}
''');
- final node = decoratedTypeAnnotation('int').node;
+ final node = decoratedTypeAnnotation('int').node!;
expect(node.hintActions, contains(HintActionKind.addNonNullableHint));
expect(
node.hintActions[HintActionKind.addNonNullableHint]
- .applyTo(super.testCode),
+ .applyTo(super.testCode!),
'''
void f(List<int/*!*/> i) {}
''');
@@ -2029,11 +2042,11 @@
await analyze('''
void f(List<int> i) {}
''');
- final node = decoratedTypeAnnotation('int').node;
+ final node = decoratedTypeAnnotation('int').node!;
expect(node.hintActions, contains(HintActionKind.addNullableHint));
expect(
node.hintActions[HintActionKind.addNullableHint]
- .applyTo(super.testCode),
+ .applyTo(super.testCode!),
'''
void f(List<int/*?*/> i) {}
''');
@@ -2043,7 +2056,7 @@
await analyze('''
class C<T extends Object> {}
''');
- var bound = decoratedTypeParameterBound('T');
+ var bound = decoratedTypeParameterBound('T')!;
expect(decoratedTypeAnnotation('Object'), same(bound));
expect(bound.node, isNot(always));
expect(bound.type, typeProvider.objectType);
@@ -2056,7 +2069,7 @@
await analyze('''
class C<T> {}
''');
- var bound = decoratedTypeParameterBound('T');
+ var bound = decoratedTypeParameterBound('T')!;
assertEdge(always, bound.node, hard: false);
expect(bound.type, same(typeProvider.objectType));
}
@@ -2070,16 +2083,16 @@
// from the ones in the typedef (they will be unified by the edge builder).
// This is necessary because there is no guarantee of whether the typedef or
// its usage will be visited first.
- var typedefDecoratedType = variables
- .decoratedElementType(findElement.typeAlias('F').aliasedElement);
+ var typedefDecoratedType = variables!
+ .decoratedElementType(findElement.typeAlias('F').aliasedElement!);
var decoratedType = decoratedTypeAnnotation('F<int>');
expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
expect(decoratedType.node, isNot(same(typedefDecoratedType.node)));
- _assertType(decoratedType.returnType.type, 'int');
+ _assertType(decoratedType.returnType!.type!, 'int');
expect(
- decoratedType.returnType.node, TypeMatcher<NullabilityNodeMutable>());
- expect(decoratedType.returnType.node,
- isNot(same(typedefDecoratedType.returnType.node)));
+ decoratedType.returnType!.node, TypeMatcher<NullabilityNodeMutable>());
+ expect(decoratedType.returnType!.node,
+ isNot(same(typedefDecoratedType.returnType!.node)));
}
Future<void> test_typedef_reference_generic_uninstantiated() async {
@@ -2091,23 +2104,23 @@
// from the ones in the typedef (they will be unified by the edge builder).
// This is necessary because there is no guarantee of whether the typedef or
// its usage will be visited first.
- var typedefDecoratedType = variables
- .decoratedElementType(findElement.typeAlias('F').aliasedElement);
+ var typedefDecoratedType = variables!
+ .decoratedElementType(findElement.typeAlias('F').aliasedElement!);
var decoratedType = decoratedTypeAnnotation('F f');
expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
expect(decoratedType.node, isNot(same(typedefDecoratedType.node)));
- _assertType(decoratedType.returnType.type, 'T');
+ _assertType(decoratedType.returnType!.type!, 'T');
expect(
- decoratedType.returnType.node, TypeMatcher<NullabilityNodeMutable>());
- expect(decoratedType.returnType.node,
- isNot(same(typedefDecoratedType.returnType.node)));
+ decoratedType.returnType!.node, TypeMatcher<NullabilityNodeMutable>());
+ expect(decoratedType.returnType!.node,
+ isNot(same(typedefDecoratedType.returnType!.node)));
var decoratedTypeFormalBound = decoratedTypeParameterBounds
- .get((decoratedType.type as FunctionType).typeFormals[0]);
- _assertType(decoratedTypeFormalBound.type, 'num');
- expect(decoratedTypeFormalBound.node.displayName,
+ .get((decoratedType.type as FunctionType).typeFormals[0])!;
+ _assertType(decoratedTypeFormalBound.type!, 'num');
+ expect(decoratedTypeFormalBound.node!.displayName,
'bound of type formal T of f (test.dart:2:1)');
var decoratedTypedefTypeFormalBound = decoratedTypeParameterBounds
- .get((typedefDecoratedType.type as FunctionType).typeFormals[0]);
+ .get((typedefDecoratedType.type as FunctionType).typeFormals[0])!;
expect(decoratedTypeFormalBound.node,
isNot(same(decoratedTypedefTypeFormalBound.node)));
}
@@ -2121,26 +2134,26 @@
// from the ones in the typedef (they will be unified by the edge builder).
// This is necessary because there is no guarantee of whether the typedef or
// its usage will be visited first.
- var typedefDecoratedType = variables
- .decoratedElementType(findElement.typeAlias('F').aliasedElement);
+ var typedefDecoratedType = variables!
+ .decoratedElementType(findElement.typeAlias('F').aliasedElement!);
var decoratedType = decoratedTypeAnnotation('F f');
expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
expect(decoratedType.node, isNot(same(typedefDecoratedType.node)));
- _assertType(decoratedType.returnType.type, 'int');
+ _assertType(decoratedType.returnType!.type!, 'int');
expect(
- decoratedType.returnType.node, TypeMatcher<NullabilityNodeMutable>());
- expect(decoratedType.returnType.node,
- isNot(same(typedefDecoratedType.returnType.node)));
- expect(typedefDecoratedType.returnType.node.displayName,
+ decoratedType.returnType!.node, TypeMatcher<NullabilityNodeMutable>());
+ expect(decoratedType.returnType!.node,
+ isNot(same(typedefDecoratedType.returnType!.node)));
+ expect(typedefDecoratedType.returnType!.node!.displayName,
'return type of F (test.dart:1:9)');
- expect(decoratedType.returnType.node.displayName,
+ expect(decoratedType.returnType!.node!.displayName,
'return type of f (test.dart:2:1)');
- _assertType(decoratedType.positionalParameters[0].type, 'String');
- expect(decoratedType.positionalParameters[0].node,
+ _assertType(decoratedType.positionalParameters![0]!.type!, 'String');
+ expect(decoratedType.positionalParameters![0]!.node,
TypeMatcher<NullabilityNodeMutable>());
- expect(decoratedType.positionalParameters[0].node,
- isNot(same(typedefDecoratedType.positionalParameters[0].node)));
- expect(decoratedType.positionalParameters[0].node.displayName,
+ expect(decoratedType.positionalParameters![0]!.node,
+ isNot(same(typedefDecoratedType.positionalParameters![0]!.node)));
+ expect(decoratedType.positionalParameters![0]!.node!.displayName,
'parameter 0 of f (test.dart:2:1)');
}
@@ -2154,7 +2167,7 @@
// This is necessary because there is no guarantee of whether the typedef or
// its usage will be visited first.
var decoratedType = decoratedTypeAnnotation('F f');
- expect(decoratedType.namedParameters['s'].node.displayName,
+ expect(decoratedType.namedParameters!['s']!.node!.displayName,
'parameter s of f (test.dart:2:1)');
}
@@ -2168,9 +2181,9 @@
// This is necessary because there is no guarantee of whether the typedef or
// its usage will be visited first.
var decoratedType = decoratedTypeAnnotation('F f');
- expect(decoratedType.positionalParameters[0].node.displayName,
+ expect(decoratedType.positionalParameters![0]!.node!.displayName,
'parameter 0 of f (test.dart:2:1)');
- expect(decoratedType.positionalParameters[1].node.displayName,
+ expect(decoratedType.positionalParameters![1]!.node!.displayName,
'parameter 1 of f (test.dart:2:1)');
}
@@ -2185,40 +2198,40 @@
Future<void> test_variableDeclaration_late_final_hint_simple() async {
await analyze('/*late final*/ int i;');
expect(
- variables.getLateHint(
- testSource, findNode.variableDeclarationList('int i')),
+ variables!
+ .getLateHint(testSource, findNode.variableDeclarationList('int i')),
isNotNull);
}
Future<void> test_variableDeclaration_late_hint_after_metadata() async {
await analyze('@deprecated /*late*/ int i;');
expect(
- variables.getLateHint(
- testSource, findNode.variableDeclarationList('int i')),
+ variables!
+ .getLateHint(testSource, findNode.variableDeclarationList('int i')),
isNotNull);
}
Future<void> test_variableDeclaration_late_hint_multiple_comments() async {
await analyze('/*other*/ /*late*/ int i;');
expect(
- variables.getLateHint(
- testSource, findNode.variableDeclarationList('int i')),
+ variables!
+ .getLateHint(testSource, findNode.variableDeclarationList('int i')),
isNotNull);
}
Future<void> test_variableDeclaration_late_hint_simple() async {
await analyze('/*late*/ int i;');
expect(
- variables.getLateHint(
- testSource, findNode.variableDeclarationList('int i')),
+ variables!
+ .getLateHint(testSource, findNode.variableDeclarationList('int i')),
isNotNull);
}
Future<void> test_variableDeclaration_late_hint_with_spaces() async {
await analyze('/* late */ int i;');
expect(
- variables.getLateHint(
- testSource, findNode.variableDeclarationList('int i')),
+ variables!
+ .getLateHint(testSource, findNode.variableDeclarationList('int i')),
isNotNull);
}
diff --git a/pkg/nnbd_migration/test/nullability_node_test.dart b/pkg/nnbd_migration/test/nullability_node_test.dart
index 7f46e17..849333d 100644
--- a/pkg/nnbd_migration/test/nullability_node_test.dart
+++ b/pkg/nnbd_migration/test/nullability_node_test.dart
@@ -20,9 +20,9 @@
final graph = NullabilityGraphForTesting();
/// A list of all edges that couldn't be satisfied. May contain duplicates.
- List<NullabilityEdge> unsatisfiedEdges;
+ late List<NullabilityEdge> unsatisfiedEdges;
- List<NullabilityNodeForSubstitution> unsatisfiedSubstitutions;
+ List<NullabilityNodeForSubstitution>? unsatisfiedSubstitutions;
NullabilityNode get always => graph.always;
@@ -53,7 +53,7 @@
unsatisfiedSubstitutions = propagationResult.unsatisfiedSubstitutions;
}
- NullabilityNode subst(NullabilityNode inner, NullabilityNode outer) {
+ NullabilityNode subst(NullabilityNode? inner, NullabilityNode? outer) {
return NullabilityNode.forSubstitution(inner, outer);
}
@@ -747,22 +747,22 @@
graph.union(x, y, _TestEdgeOrigin());
}
- NullabilityNode _downstreamCauseNode(NullabilityNode node) =>
+ NullabilityNode? _downstreamCauseNode(NullabilityNode node) =>
(node.whyNullable as SimpleDownstreamPropagationStep).edge.sourceNode;
NullabilityNode _upstreamCauseNode(NullabilityNode node) =>
- node.whyNotNullable.principalCause.node;
+ node.whyNotNullable!.principalCause!.node;
}
class _TestEdgeOrigin implements EdgeOrigin {
@override
- CodeReference get codeReference => null;
+ CodeReference? get codeReference => null;
@override
String get description => 'Test edge';
@override
- EdgeOriginKind get kind => null;
+ EdgeOriginKind? get kind => null;
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
diff --git a/pkg/nnbd_migration/test/preview/preview_site_test.dart b/pkg/nnbd_migration/test/preview/preview_site_test.dart
index 313c365..aeee9da 100644
--- a/pkg/nnbd_migration/test/preview/preview_site_test.dart
+++ b/pkg/nnbd_migration/test/preview/preview_site_test.dart
@@ -44,9 +44,9 @@
final migrationInfo =
MigrationInfo({}, {}, resourceProvider.pathContext, null);
state = MigrationState(
- null, null, dartfixListener, null, {}, (String path) => true);
- state.pathMapper = PathMapper(resourceProvider);
- state.migrationInfo = migrationInfo;
+ null, null, dartfixListener, null, {}, (String? path) => true);
+ state!.pathMapper = PathMapper(resourceProvider);
+ state!.migrationInfo = migrationInfo;
logger = TestLogger(false /*isVerbose*/);
site = PreviewSite(state, () async {
return state;
@@ -63,7 +63,7 @@
file.writeAsStringSync(content);
site.unitInfoMap[path] = UnitInfo(path)..diskContent = content;
// Add a source change for analysis_options, which has no UnitInfo.
- dartfixListener.addSourceFileEdit(
+ dartfixListener!.addSourceFileEdit(
'enable experiment',
Location(analysisOptionsPath, 9, 0, 1, 9, 1, 9),
SourceFileEdit(analysisOptionsPath, 0, edits: [
@@ -75,7 +75,7 @@
analyzer:
enable-experiment:
- non-nullable''');
- expect(state.hasBeenApplied, true);
+ expect(state!.hasBeenApplied, true);
}
void test_applyChangesEmpty() {
@@ -83,7 +83,7 @@
file.writeAsStringSync('void main() {}');
site.performApply([]);
expect(file.readAsStringSync(), 'void main() {}');
- expect(state.hasBeenApplied, true);
+ expect(state!.hasBeenApplied, true);
}
void test_applyChangesTwiceThrows() {
@@ -98,13 +98,13 @@
..diskContent = '// different content';
final currentContent = 'void main() {}';
file.writeAsStringSync(currentContent);
- dartfixListener.addSourceFileEdit(
+ dartfixListener!.addSourceFileEdit(
'test change',
Location(path, 10, 0, 1, 10, 1, 10),
SourceFileEdit(path, 0, edits: [SourceEdit(10, 0, 'List args')]));
expect(() => site.performApply([]), throwsA(isA<StateError>()));
expect(file.readAsStringSync(), currentContent);
- expect(state.hasBeenApplied, false);
+ expect(state!.hasBeenApplied, false);
}
void test_applyMultipleChanges() {
@@ -113,7 +113,7 @@
final content = 'void main() {}';
file.writeAsStringSync(content);
site.unitInfoMap[path] = UnitInfo(path)..diskContent = content;
- dartfixListener.addSourceFileEdit(
+ dartfixListener!.addSourceFileEdit(
'test change',
Location(path, 10, 0, 1, 10, 1, 10),
SourceFileEdit(path, 0, edits: [
@@ -125,7 +125,7 @@
void main(List args) {
print(args);
}''');
- expect(state.hasBeenApplied, true);
+ expect(state!.hasBeenApplied, true);
}
void test_applySingleChange() {
@@ -134,13 +134,13 @@
final content = 'void main() {}';
file.writeAsStringSync(content);
site.unitInfoMap[path] = UnitInfo(path)..diskContent = content;
- dartfixListener.addSourceFileEdit(
+ dartfixListener!.addSourceFileEdit(
'test change',
Location(path, 10, 0, 1, 10, 1, 10),
SourceFileEdit(path, 0, edits: [SourceEdit(10, 0, 'List args')]));
site.performApply([]);
expect(file.readAsStringSync(), 'void main(List args) {}');
- expect(state.hasBeenApplied, true);
+ expect(state!.hasBeenApplied, true);
}
void test_optOutOfNullSafety_blankLines() {
@@ -238,8 +238,8 @@
file.writeAsStringSync(content);
performEdit(path, 3, '/*?*/');
expect(file.readAsStringSync(), 'int/*?*/ foo() {}');
- expect(state.hasBeenApplied, false);
- expect(state.needsRerun, true);
+ expect(state!.hasBeenApplied, false);
+ expect(state!.needsRerun, true);
expect(unitInfo.content, 'int/*?*/ foo() {}');
}
@@ -252,15 +252,15 @@
file.writeAsStringSync(currentContent);
expect(() => performEdit(path, 0, 'foo'), throwsA(isA<StateError>()));
expect(file.readAsStringSync(), currentContent);
- expect(state.hasBeenApplied, false);
+ expect(state!.hasBeenApplied, false);
}
}
mixin PreviewSiteTestMixin {
- PreviewSite site;
- DartFixListener dartfixListener;
- MigrationState state;
- TestLogger logger;
+ late PreviewSite site;
+ DartFixListener? dartfixListener;
+ MigrationState? state;
+ TestLogger? logger;
Future<void> performEdit(String path, int offset, String replacement) {
final pathUri = Uri.file(path).path;
@@ -273,12 +273,12 @@
@reflectiveTest
class PreviewSiteWithEngineTest extends NnbdMigrationTestBase
with ResourceProviderMixin, PreviewSiteTestMixin {
- MigrationInfo migrationInfo;
+ MigrationInfo? migrationInfo;
Future<void> setUpMigrationInfo(Map<String, String> files,
- {bool Function(String) shouldBeMigratedFunction,
- Iterable<String> pathsToProcess}) async {
- shouldBeMigratedFunction ??= (String path) => true;
+ {bool Function(String?)? shouldBeMigratedFunction,
+ Iterable<String>? pathsToProcess}) async {
+ shouldBeMigratedFunction ??= (String? path) => true;
pathsToProcess ??= files.keys;
await buildInfoForTestFiles(files,
includedRoot: projectPath,
@@ -289,9 +289,9 @@
MigrationInfo(infos, {}, resourceProvider.pathContext, projectPath);
state = MigrationState(
null, null, dartfixListener, null, {}, shouldBeMigratedFunction);
- nodeMapper = state.nodeMapper;
- state.pathMapper = PathMapper(resourceProvider);
- state.migrationInfo = migrationInfo;
+ nodeMapper = state!.nodeMapper;
+ state!.pathMapper = PathMapper(resourceProvider);
+ state!.migrationInfo = migrationInfo;
logger = TestLogger(false /*isVerbose*/);
site = PreviewSite(state, () async {
return state;
@@ -327,25 +327,29 @@
int/*?*/? y = x;
''');
assertRegion(
- region: unitInfo.regions[0], offset: unitInfo.content.indexOf('? x'));
+ region: unitInfo.regions[0], offset: unitInfo.content!.indexOf('? x'));
assertRegion(
- region: unitInfo.regions[1], offset: unitInfo.content.indexOf('? y'));
+ region: unitInfo.regions[1], offset: unitInfo.content!.indexOf('? y'));
final targets = List<NavigationTarget>.from(unitInfo.targets);
assertInTargets(
targets: targets,
- offset: unitInfo.content.indexOf('x'),
+ offset: unitInfo.content!.indexOf('x'),
offsetMapper: unitInfo.offsetMapper);
assertInTargets(
targets: targets,
- offset: unitInfo.content.indexOf('y'),
+ offset: unitInfo.content!.indexOf('y'),
offsetMapper: unitInfo.offsetMapper);
var trace = unitInfo.regions[1].traces[0];
- assertTraceEntry(unitInfo, trace.entries[0], 'y',
- unitInfo.content.indexOf('int/*?*/? y'), contains('y (test.dart:2:1)'));
+ assertTraceEntry(
+ unitInfo,
+ trace.entries[0],
+ 'y',
+ unitInfo.content!.indexOf('int/*?*/? y'),
+ contains('y (test.dart:2:1)'));
assertTraceEntry(unitInfo, trace.entries[1], 'y',
- unitInfo.content.indexOf('= x;') + '= '.length, contains('data flow'));
- expect(state.hasBeenApplied, false);
- expect(state.needsRerun, true);
+ unitInfo.content!.indexOf('= x;') + '= '.length, contains('data flow'));
+ expect(state!.hasBeenApplied, false);
+ expect(state!.needsRerun, true);
}
void test_applyHintAction_removeHint() async {
@@ -378,14 +382,14 @@
assertRegion(
kind: NullabilityFixKind.typeNotMadeNullable,
region: unitInfo.regions[0],
- offset: unitInfo.content.indexOf(' y'));
+ offset: unitInfo.content!.indexOf(' y'));
final targets = List<NavigationTarget>.from(unitInfo.targets);
assertInTargets(
targets: targets,
- offset: unitInfo.content.indexOf('x'),
+ offset: unitInfo.content!.indexOf('x'),
offsetMapper: unitInfo.offsetMapper);
- expect(state.hasBeenApplied, false);
- expect(state.needsRerun, true);
+ expect(state!.hasBeenApplied, false);
+ expect(state!.needsRerun, true);
}
void test_applyMigration_doNotOptOutFileNotInPathsToProcess() async {
@@ -394,7 +398,7 @@
final content = 'void main() {}';
await setUpMigrationInfo({pathA: content, pathB: content},
// Neither [pathA] nor [[pathB] should be migrated.
- shouldBeMigratedFunction: (String path) => false,
+ shouldBeMigratedFunction: (String? path) => false,
pathsToProcess: [pathA]);
site.unitInfoMap[pathA] = UnitInfo(pathA)
..diskContent = content
@@ -405,7 +409,7 @@
..wasExplicitlyOptedOut = false
..migrationStatus = UnitMigrationStatus.optingOut;
var navigationTree =
- NavigationTreeRenderer(migrationInfo, state.pathMapper).render();
+ NavigationTreeRenderer(migrationInfo, state!.pathMapper).render();
site.performApply(navigationTree);
expect(getFile(pathA).readAsStringSync(), '''
// @dart=2.9
@@ -424,15 +428,15 @@
site.unitInfoMap[path] = UnitInfo(path)
..diskContent = content
..wasExplicitlyOptedOut = true;
- dartfixListener.addSourceFileEdit(
+ dartfixListener!.addSourceFileEdit(
'remove DLV comment',
Location(path, 0, 14, 1, 1, 3, 1),
SourceFileEdit(path, 0, edits: [SourceEdit(0, 14, '')]));
var navigationTree =
- NavigationTreeRenderer(migrationInfo, state.pathMapper).render();
+ NavigationTreeRenderer(migrationInfo, state!.pathMapper).render();
site.performApply(navigationTree);
expect(getFile(path).readAsStringSync(), 'void main() {}');
- expect(logger.stdoutBuffer.toString(), contains('''
+ expect(logger!.stdoutBuffer.toString(), contains('''
Migrated 1 file:
${convertPath('lib/a.dart')}
'''));
@@ -446,13 +450,13 @@
..diskContent = content
..wasExplicitlyOptedOut = false;
var navigationTree =
- NavigationTreeRenderer(migrationInfo, state.pathMapper).render();
+ NavigationTreeRenderer(migrationInfo, state!.pathMapper).render();
var libDir = navigationTree.single as NavigationTreeDirectoryNode;
- (libDir.subtree.single as NavigationTreeFileNode).migrationStatus =
+ (libDir.subtree!.single as NavigationTreeFileNode).migrationStatus =
UnitMigrationStatus.optingOut;
site.performApply(navigationTree);
expect(getFile(path).readAsStringSync(), '// @dart=2.9');
- expect(logger.stdoutBuffer.toString(), contains('''
+ expect(logger!.stdoutBuffer.toString(), contains('''
Opted 1 file out of null safety with a new Dart language version comment:
${convertPath('lib/a.dart')}
'''));
@@ -465,21 +469,21 @@
site.unitInfoMap[path] = UnitInfo(path)
..diskContent = content
..wasExplicitlyOptedOut = false;
- dartfixListener.addSourceFileEdit(
+ dartfixListener!.addSourceFileEdit(
'test change',
Location(path, 10, 0, 1, 10, 1, 10),
SourceFileEdit(path, 0, edits: [SourceEdit(10, 0, 'List args')]));
var navigationTree =
- NavigationTreeRenderer(migrationInfo, state.pathMapper).render();
+ NavigationTreeRenderer(migrationInfo, state!.pathMapper).render();
var libDir = navigationTree.single as NavigationTreeDirectoryNode;
- (libDir.subtree.single as NavigationTreeFileNode).migrationStatus =
+ (libDir.subtree!.single as NavigationTreeFileNode).migrationStatus =
UnitMigrationStatus.optingOut;
site.performApply(navigationTree);
expect(getFile(path).readAsStringSync(), '''
// @dart=2.9
void main() {}''');
- expect(logger.stdoutBuffer.toString(), contains('''
+ expect(logger!.stdoutBuffer.toString(), contains('''
Opted 1 file out of null safety with a new Dart language version comment:
${convertPath('lib/a.dart')}
'''));
@@ -493,16 +497,16 @@
..diskContent = content
..wasExplicitlyOptedOut = false;
var navigationTree =
- NavigationTreeRenderer(migrationInfo, state.pathMapper).render();
+ NavigationTreeRenderer(migrationInfo, state!.pathMapper).render();
var libDir = navigationTree.single as NavigationTreeDirectoryNode;
- (libDir.subtree.single as NavigationTreeFileNode).migrationStatus =
+ (libDir.subtree!.single as NavigationTreeFileNode).migrationStatus =
UnitMigrationStatus.optingOut;
site.performApply(navigationTree);
expect(getFile(path).readAsStringSync(), '''
// @dart=2.9
void main() {}''');
- expect(logger.stdoutBuffer.toString(), contains('''
+ expect(logger!.stdoutBuffer.toString(), contains('''
Opted 1 file out of null safety with a new Dart language version comment:
${convertPath('lib/a.dart')}
'''));
@@ -519,18 +523,18 @@
site.unitInfoMap[pathB] = UnitInfo(pathB)
..diskContent = content
..wasExplicitlyOptedOut = false;
- dartfixListener.addSourceFileEdit(
+ dartfixListener!.addSourceFileEdit(
'test change',
Location(pathA, 10, 0, 1, 10, 1, 10),
SourceFileEdit(pathA, 0, edits: [SourceEdit(10, 0, 'List args')]));
- dartfixListener.addSourceFileEdit(
+ dartfixListener!.addSourceFileEdit(
'test change',
Location(pathB, 10, 0, 1, 10, 1, 10),
SourceFileEdit(pathB, 0, edits: [SourceEdit(10, 0, 'List args')]));
var navigationTree =
- NavigationTreeRenderer(migrationInfo, state.pathMapper).render();
+ NavigationTreeRenderer(migrationInfo, state!.pathMapper).render();
var libDir = navigationTree.single as NavigationTreeDirectoryNode;
- (libDir.subtree[0] as NavigationTreeFileNode).migrationStatus =
+ (libDir.subtree![0] as NavigationTreeFileNode).migrationStatus =
UnitMigrationStatus.optingOut;
site.performApply(navigationTree);
expect(getFile(pathA).readAsStringSync(), '''
@@ -539,7 +543,7 @@
void main() {}''');
expect(getFile(pathB).readAsStringSync(), '''
void main(List args) {}''');
- expect(logger.stdoutBuffer.toString(), contains('''
+ expect(logger!.stdoutBuffer.toString(), contains('''
Migrated 1 file:
${convertPath('lib/b.dart')}
Opted 1 file out of null safety with a new Dart language version comment:
@@ -557,18 +561,18 @@
site.unitInfoMap[path] = UnitInfo(path)
..diskContent = content
..wasExplicitlyOptedOut = true;
- dartfixListener.addSourceFileEdit(
+ dartfixListener!.addSourceFileEdit(
'remove DLV comment',
Location(path, 0, 14, 1, 1, 3, 1),
SourceFileEdit(path, 0, edits: [SourceEdit(0, 14, '')]));
var navigationTree =
- NavigationTreeRenderer(migrationInfo, state.pathMapper).render();
+ NavigationTreeRenderer(migrationInfo, state!.pathMapper).render();
var libDir = navigationTree.single as NavigationTreeDirectoryNode;
- (libDir.subtree.single as NavigationTreeFileNode).migrationStatus =
+ (libDir.subtree!.single as NavigationTreeFileNode).migrationStatus =
UnitMigrationStatus.optingOut;
site.performApply(navigationTree);
expect(getFile(path).readAsStringSync(), content);
- expect(logger.stdoutBuffer.toString(), contains('''
+ expect(logger!.stdoutBuffer.toString(), contains('''
Kept 1 file opted out of null safety:
${convertPath('lib/a.dart')}
'''));
@@ -603,24 +607,28 @@
int/*?*/? y = x;
''');
assertRegion(
- region: unitInfo.regions[0], offset: unitInfo.content.indexOf('? x'));
+ region: unitInfo.regions[0], offset: unitInfo.content!.indexOf('? x'));
assertRegion(
- region: unitInfo.regions[1], offset: unitInfo.content.indexOf('? y'));
+ region: unitInfo.regions[1], offset: unitInfo.content!.indexOf('? y'));
final targets = List<NavigationTarget>.from(unitInfo.targets);
assertInTargets(
targets: targets,
- offset: unitInfo.content.indexOf('x'),
+ offset: unitInfo.content!.indexOf('x'),
offsetMapper: unitInfo.offsetMapper);
assertInTargets(
targets: targets,
- offset: unitInfo.content.indexOf('y'),
+ offset: unitInfo.content!.indexOf('y'),
offsetMapper: unitInfo.offsetMapper);
var trace = unitInfo.regions[1].traces[0];
- assertTraceEntry(unitInfo, trace.entries[0], 'y',
- unitInfo.content.indexOf('int/*?*/? y'), contains('y (test.dart:2:1)'));
+ assertTraceEntry(
+ unitInfo,
+ trace.entries[0],
+ 'y',
+ unitInfo.content!.indexOf('int/*?*/? y'),
+ contains('y (test.dart:2:1)'));
assertTraceEntry(unitInfo, trace.entries[1], 'y',
- unitInfo.content.indexOf('= x;') + '= '.length, contains('data flow'));
- expect(state.hasBeenApplied, false);
- expect(state.needsRerun, true);
+ unitInfo.content!.indexOf('= x;') + '= '.length, contains('data flow'));
+ expect(state!.hasBeenApplied, false);
+ expect(state!.needsRerun, true);
}
}
diff --git a/pkg/nnbd_migration/test/utilities/multi_future_tracker_test.dart b/pkg/nnbd_migration/test/utilities/multi_future_tracker_test.dart
index a0fb113..634a347 100644
--- a/pkg/nnbd_migration/test/utilities/multi_future_tracker_test.dart
+++ b/pkg/nnbd_migration/test/utilities/multi_future_tracker_test.dart
@@ -16,7 +16,7 @@
@reflectiveTest
class MultiFutureTrackerTest {
- MultiFutureTracker testTracker;
+ MultiFutureTracker? testTracker;
tearDown() {
// This will leak futures on certain kinds of test failures. But it is
@@ -28,16 +28,16 @@
var completer1 = Completer();
testTracker = MultiFutureTracker(1);
- await testTracker.addFutureFromClosure(() => completer1.future);
+ await testTracker!.addFutureFromClosure(() => completer1.future);
// The second future added shouldn't be executing until the first
// future is complete.
- var secondInQueue = testTracker.addFutureFromClosure(() async {
+ var secondInQueue = testTracker!.addFutureFromClosure(() async {
expect(completer1.isCompleted, isTrue);
});
completer1.complete();
await secondInQueue;
- return await testTracker.wait();
+ return await testTracker!.wait();
}
Future<void> test_doesNotBlockWithoutLimit() async {
@@ -45,23 +45,23 @@
// Limit is set above the number of futures we are adding.
testTracker = MultiFutureTracker(10);
- await testTracker.addFutureFromClosure(() => completer1.future);
+ await testTracker!.addFutureFromClosure(() => completer1.future);
// The second future added should be executing even though the first
// future is not complete. A test failure will time out.
- await testTracker.addFutureFromClosure(() async {
+ await testTracker!.addFutureFromClosure(() async {
expect(completer1.isCompleted, isFalse);
completer1.complete();
});
- return await testTracker.wait();
+ return await testTracker!.wait();
}
Future<void> test_runsSeriallyAtLowLimit() async {
var completer1 = Completer();
testTracker = MultiFutureTracker(1);
- var runFuture1 = testTracker.runFutureFromClosure(() => completer1.future);
- var runFuture2 = testTracker.runFutureFromClosure(() => null);
+ var runFuture1 = testTracker!.runFutureFromClosure(() => completer1.future);
+ var runFuture2 = testTracker!.runFutureFromClosure(() => null);
// Both futures _should_ timeout.
await expectLater(runFuture1.timeout(Duration(milliseconds: 1)),
@@ -79,9 +79,9 @@
Future<void> test_returnsValueFromRun() async {
testTracker = MultiFutureTracker(1);
+ await expectLater(await testTracker!.runFutureFromClosure(() async => true),
+ equals(true));
await expectLater(
- await testTracker.runFutureFromClosure(() async => true), equals(true));
- await expectLater(
- await testTracker.runFutureFromClosure(() => true), equals(true));
+ await testTracker!.runFutureFromClosure(() => true), equals(true));
}
}
diff --git a/pkg/nnbd_migration/test/utilities/scoped_set_test.dart b/pkg/nnbd_migration/test/utilities/scoped_set_test.dart
index 9facb62..edaab6b 100644
--- a/pkg/nnbd_migration/test/utilities/scoped_set_test.dart
+++ b/pkg/nnbd_migration/test/utilities/scoped_set_test.dart
@@ -36,7 +36,7 @@
test_doScoped_actionThrows() {
final set = ScopedSet<int>();
- bool threw;
+ bool? threw;
try {
set.doScoped(action: () {
set.add(0);
diff --git a/pkg/nnbd_migration/test/utilities/subprocess_launcher_test.dart b/pkg/nnbd_migration/test/utilities/subprocess_launcher_test.dart
index b1980da..7b05cfa 100644
--- a/pkg/nnbd_migration/test/utilities/subprocess_launcher_test.dart
+++ b/pkg/nnbd_migration/test/utilities/subprocess_launcher_test.dart
@@ -17,13 +17,13 @@
@reflectiveTest
class SubprocessLauncherTest {
- Function(String) outputCallback;
- List<String> output;
- Directory tempDir;
+ Function(String)? outputCallback;
+ List<String>? output;
+ late Directory tempDir;
void setUp() async {
output = [];
- outputCallback = output.add;
+ outputCallback = output!.add;
tempDir = await Directory.systemTemp.createTemp();
}
diff --git a/pkg/nnbd_migration/test/utilities/where_or_null_transformer_test.dart b/pkg/nnbd_migration/test/utilities/where_or_null_transformer_test.dart
index 8360432..748d373 100644
--- a/pkg/nnbd_migration/test/utilities/where_or_null_transformer_test.dart
+++ b/pkg/nnbd_migration/test/utilities/where_or_null_transformer_test.dart
@@ -18,7 +18,7 @@
@reflectiveTest
class WhereOrNullTransformerTest extends AbstractSingleUnitTest {
- WhereOrNullTransformer transformer;
+ late WhereOrNullTransformer transformer;
TypeProvider get typeProvider => testAnalysisResult.typeProvider;
@@ -35,7 +35,7 @@
''');
var orElseExpression = findNode.functionExpression('() => null');
var transformationInfo =
- transformer.tryTransformOrElseArgument(orElseExpression);
+ transformer.tryTransformOrElseArgument(orElseExpression)!;
expect(transformationInfo, isNotNull);
expect(transformationInfo.methodInvocation,
same(findNode.methodInvocation('firstWhere')));
@@ -53,7 +53,7 @@
''');
var orElseExpression = findNode.functionExpression('() => null');
var transformationInfo =
- transformer.tryTransformOrElseArgument(orElseExpression);
+ transformer.tryTransformOrElseArgument(orElseExpression)!;
expect(transformationInfo, isNotNull);
expect(transformationInfo.methodInvocation,
same(findNode.methodInvocation('firstWhere((')));
diff --git a/pkg/nnbd_migration/tool/codegen/extract_resource.dart b/pkg/nnbd_migration/tool/codegen/extract_resource.dart
index 3b60593..60b1719 100644
--- a/pkg/nnbd_migration/tool/codegen/extract_resource.dart
+++ b/pkg/nnbd_migration/tool/codegen/extract_resource.dart
@@ -23,8 +23,8 @@
fail('Unexpected extra arguments', showUsage: true);
}
bool list = argResults['list'] as bool;
- String path = argResults['path'] as String;
- String resource = argResults['resource'] as String;
+ String? path = argResults['path'] as String?;
+ String? resource = argResults['resource'] as String?;
if (list && resource != null) {
fail('Only one of --resource and --list may be provided');
} else if (!list && resource == null) {
@@ -72,11 +72,11 @@
Uint8List decodeVariableDeclaration(VariableDeclaration variable) {
var initializer = variable.initializer as StringLiteral;
- var stringValue = initializer.stringValue;
+ var stringValue = initializer.stringValue!;
return base64.decode(stringValue.replaceAll('\n', '').trim());
}
-void fail(String message, {bool showUsage = false}) {
+void fail(String? message, {bool showUsage = false}) {
if (message != null) {
stderr.writeln(message);
}
@@ -94,7 +94,7 @@
/// Tries to guess the location of `resources.g.dart` using optional [pathHint]
/// as a starting point.
-File locateResourcesFile(String pathHint) {
+File locateResourcesFile(String? pathHint) {
pathHint ??= '.';
final pathParts =
'pkg/nnbd_migration/lib/src/front_end/resources/resources.g.dart'
diff --git a/pkg/nnbd_migration/tool/codegen/generate_resources.dart b/pkg/nnbd_migration/tool/codegen/generate_resources.dart
index f80e0c31..3dab62f 100644
--- a/pkg/nnbd_migration/tool/codegen/generate_resources.dart
+++ b/pkg/nnbd_migration/tool/codegen/generate_resources.dart
@@ -12,7 +12,6 @@
import 'package:args/args.dart';
import 'package:crypto/crypto.dart';
-import 'package:meta/meta.dart';
import 'package:path/path.dart' as path;
void main(List<String> args) async {
@@ -41,13 +40,13 @@
}
bool verify = argResults['verify'] as bool;
- bool dev = argResults['dev'] as bool;
+ bool? dev = argResults['dev'] as bool?;
if (verify) {
verifyResourcesGDartGenerated();
} else {
await compileWebFrontEnd(
- devMode: dev, dart2jsPath: dart2jsPath(argResults));
+ devMode: dev!, dart2jsPath: dart2jsPath(argResults)!);
print('');
@@ -56,9 +55,9 @@
}
/// Returns the dart2jsPath, either from [argResults] or the Platform.
-String dart2jsPath(ArgResults argResults) {
+String? dart2jsPath(ArgResults argResults) {
if (argResults.wasParsed('dart2js_path')) {
- return argResults['dart2js_path'] as String;
+ return argResults['dart2js_path'] as String?;
} else {
var sdkBinDir = path.dirname(Platform.resolvedExecutable);
var dart2jsBinary = Platform.isWindows ? 'dart2js.bat' : 'dart2js';
@@ -103,7 +102,7 @@
}
Future<void> compileWebFrontEnd(
- {@required bool devMode, @required String dart2jsPath}) async {
+ {required bool devMode, required String dart2jsPath}) async {
// dart2js -m -o output source
var process = await Process.start(dart2jsPath, [
devMode ? '-O1' : '-m',
@@ -157,10 +156,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-// This file is explicitly opted in to null safety since it contains `String?`
-// types, even though the rest of the migration tool isn't migrated yet.
-// @dart=2.12
-
// This file is generated; don't edit it directly.
//
// See $filePath for how
@@ -244,7 +239,7 @@
print('Verifying that ${path.basename(resourcesFile.path)} is up-to-date...');
// Find the hashes for the last generated version of resources.g.dart.
- var resourceHashes = <String, String>{};
+ var resourceHashes = <String?, String?>{};
// highlight_css md5 is 'fb012626bafd286510d32da815dae448'
var hashPattern = RegExp(r"// (\S+) md5 is '(\S+)'");
for (var match in hashPattern.allMatches(resourcesFile.readAsStringSync())) {
diff --git a/pkg/nnbd_migration/tool/src/package.dart b/pkg/nnbd_migration/tool/src/package.dart
index d8dd458..a50b8c3 100644
--- a/pkg/nnbd_migration/tool/src/package.dart
+++ b/pkg/nnbd_migration/tool/src/package.dart
@@ -12,16 +12,16 @@
/// Return a resolved path including the home directory in place of tilde
/// references.
String resolveTildePath(String originalPath) {
- if (originalPath == null || !originalPath.startsWith('~/')) {
+ if (!originalPath.startsWith('~/')) {
return originalPath;
}
String homeDir;
if (Platform.isWindows) {
- homeDir = path.absolute(Platform.environment['USERPROFILE']);
+ homeDir = path.absolute(Platform.environment['USERPROFILE']!);
} else {
- homeDir = path.absolute(Platform.environment['HOME']);
+ homeDir = path.absolute(Platform.environment['HOME']!);
}
return path.join(homeDir, originalPath.substring(2));
@@ -81,17 +81,17 @@
/// Abstraction for a package fetched via Git.
class GitPackage extends Package {
final String _clonePath;
- final bool _keepUpdated;
+ final bool? _keepUpdated;
final String label;
final Playground _playground;
GitPackage._(this._clonePath, this._playground, this._keepUpdated,
- {String name, this.label = 'master'})
+ {String? name, this.label = 'master'})
: super(name ?? _buildName(_clonePath));
static Future<GitPackage> gitPackageFactory(
- String clonePath, Playground playground, bool keepUpdated,
- {String name, String label = 'master'}) async {
+ String clonePath, Playground playground, bool? keepUpdated,
+ {String? name, String label = 'master'}) async {
GitPackage gitPackage = GitPackage._(clonePath, playground, keepUpdated,
name: name, label: label);
await gitPackage._init();
@@ -118,7 +118,7 @@
/// Initialize the package with a shallow clone. Run only once per
/// [GitPackage] instance.
Future<void> _init() async {
- if (_keepUpdated || !await Directory(packagePath).exists()) {
+ if (_keepUpdated! || !await Directory(packagePath).exists()) {
// Clone or update.
if (await Directory(packagePath).exists()) {
await launcher.runStreamed('git', ['pull'],
@@ -139,27 +139,27 @@
}
}
- SubprocessLauncher _launcher;
+ SubprocessLauncher? _launcher;
SubprocessLauncher get launcher =>
_launcher ??= SubprocessLauncher('$name-$label', _playground.env);
- String _packagePath;
+ String? _packagePath;
String get packagePath =>
// TODO(jcollins-g): allow packages from subdirectories of clones
_packagePath ??= path.join(_playground.playgroundPath, '$name-$label');
@override
- List<String> get migrationPaths => [_packagePath];
+ List<String?> get migrationPaths => [_packagePath];
@override
String toString() {
- return '$_clonePath ($label)' + (_keepUpdated ? ' [synced]' : '');
+ return '$_clonePath ($label)' + (_keepUpdated! ? ' [synced]' : '');
}
}
/// Abstraction for a package fetched via pub.
class PubPackage extends Package {
- PubPackage(String name, [String version]) : super(name) {
+ PubPackage(String name, [String? version]) : super(name) {
throw UnimplementedError();
}
@@ -183,12 +183,9 @@
_packagePath = potentialPath;
}
}
- if (_packagePath == null) {
- throw ArgumentError('Package $name not found in SDK');
- }
}
- /* late final */ String _packagePath;
+ late final String _packagePath;
@override
List<String> get migrationPaths => [_packagePath];
@@ -203,7 +200,7 @@
Package(this.name);
/// Returns the set of directories for this package.
- List<String> get migrationPaths;
+ List<String?> get migrationPaths;
@override
String toString() => name;
@@ -212,7 +209,7 @@
/// Abstraction for compiled Dart SDKs (not this repository).
class Sdk {
/// The root of the compiled SDK.
- /* late final */ String sdkPath;
+ late final String sdkPath;
Sdk(String sdkPath) {
this.sdkPath = path.canonicalize(sdkPath);
diff --git a/pkg/nnbd_migration/tool/summary_stats.dart b/pkg/nnbd_migration/tool/summary_stats.dart
index 79afca0..6e7bd51 100644
--- a/pkg/nnbd_migration/tool/summary_stats.dart
+++ b/pkg/nnbd_migration/tool/summary_stats.dart
@@ -6,7 +6,7 @@
var json =
jsonDecode(File(jsonPath).readAsStringSync()) as Map<String, Object>;
var changes = json['changes'] as Map<String, Object>;
- var byPath = changes['byPath'] as Map<String, Object>;
+ var byPath = changes['byPath'] as Map<String, Object>?;
if (args.isEmpty) {
print('''
Usage: summary_stats.dart <summary_file> [category_name]
@@ -21,10 +21,10 @@
each file with one or more suggestions categorized as [category_name].
''');
} else if (args.length == 1) {
- _printTotals(byPath);
+ _printTotals(byPath!);
} else {
var category = args[1];
- _printCategory(byPath, category);
+ _printCategory(byPath!, category);
}
}
@@ -43,8 +43,7 @@
var summary = <String, int>{};
for (var file in byPath.values.cast<Map<Object, Object>>()) {
file.forEach((category, count) {
- summary.putIfAbsent(category as String, () => 0);
- summary[category as String] += count as int;
+ summary[category as String] = (summary[category] ?? 0) + (count as int);
});
}
var categories = summary.keys.toList()..sort();
diff --git a/pkg/nnbd_migration/tool/trial_migration.dart b/pkg/nnbd_migration/tool/trial_migration.dart
index ae67ecc..4422c58 100644
--- a/pkg/nnbd_migration/tool/trial_migration.dart
+++ b/pkg/nnbd_migration/tool/trial_migration.dart
@@ -23,7 +23,7 @@
import 'src/package.dart';
void main(List<String> args) async {
- ArgResults parsedArgs = parseArguments(args);
+ ArgResults parsedArgs = parseArguments(args)!;
Sdk sdk = Sdk(parsedArgs['sdk'] as String);
@@ -42,13 +42,13 @@
var packageNames = parsedArgs['git_packages'] as Iterable<String>;
await Future.wait(packageNames.map((n) async => packages.add(
await GitPackage.gitPackageFactory(
- n, playground, parsedArgs['update'] as bool))));
+ n, playground, parsedArgs['update'] as bool?))));
- String categoryOfInterest =
+ String? categoryOfInterest =
parsedArgs.rest.isEmpty ? null : parsedArgs.rest.single;
var listener = _Listener(categoryOfInterest,
- printExceptionNodeOnly: parsedArgs['exception_node_only'] as bool);
+ printExceptionNodeOnly: parsedArgs['exception_node_only'] as bool?);
assert(listener.numExceptions == 0);
var overallStartTime = DateTime.now();
for (var package in packages) {
@@ -56,7 +56,8 @@
var startTime = DateTime.now();
listener.currentPackage = package.name;
var contextCollection = AnalysisContextCollectionImpl(
- includedPaths: package.migrationPaths, sdkPath: sdk.sdkPath);
+ includedPaths: package.migrationPaths as List<String>,
+ sdkPath: sdk.sdkPath);
var files = <String>{};
var previousExceptionCount = listener.numExceptions;
@@ -136,9 +137,9 @@
}
}
-ArgResults parseArguments(List<String> args) {
+ArgResults? parseArguments(List<String> args) {
ArgParser argParser = ArgParser();
- ArgResults parsedArgs;
+ ArgResults? parsedArgs;
argParser.addFlag('clean',
abbr: 'c',
@@ -224,15 +225,15 @@
class ExceptionCategory {
final String topOfStack;
- final List<MapEntry<String, int>> exceptionCountPerPackage;
+ final List<MapEntry<String?, int>> exceptionCountPerPackage;
- ExceptionCategory(this.topOfStack, Map<String, int> exceptions)
+ ExceptionCategory(this.topOfStack, Map<String?, int> exceptions)
: exceptionCountPerPackage = exceptions.entries.toList()
..sort((e1, e2) => e2.value.compareTo(e1.value));
int get count => exceptionCountPerPackage.length;
- List<String> get packageNames =>
+ List<String?> get packageNames =>
[for (var entry in exceptionCountPerPackage) entry.key];
Iterable<String> get packageNamesAndCounts =>
@@ -245,14 +246,14 @@
/// Set this to `true` to cause just the exception nodes to be printed when
/// `_Listener.categoryOfInterest` is non-null. Set this to `false` to cause
/// the full stack trace to be printed.
- final bool printExceptionNodeOnly;
+ final bool? printExceptionNodeOnly;
/// Set this to a non-null value to cause any exception to be printed in full
/// if its category contains the string.
- final String categoryOfInterest;
+ final String? categoryOfInterest;
/// Exception mapped to a map of packages & exception counts.
- final groupedExceptions = <String, Map<String, int>>{};
+ final groupedExceptions = <String, Map<String?, int>>{};
int numExceptions = 0;
@@ -274,7 +275,7 @@
int numOtherEdits = 0;
- String currentPackage;
+ String? currentPackage;
_Listener(this.categoryOfInterest, {this.printExceptionNodeOnly = false});
@@ -324,7 +325,7 @@
@override
void reportException(
- Source source, AstNode node, Object exception, StackTrace stackTrace) {
+ Source? source, AstNode? node, Object exception, StackTrace stackTrace) {
var category = _classifyStackTrace(stackTrace.toString().split('\n'));
String detail = '''
In file $source
@@ -332,14 +333,14 @@
Exception $exception
$stackTrace
''';
- if (categoryOfInterest != null && category.contains(categoryOfInterest)) {
- if (printExceptionNodeOnly) {
+ if (categoryOfInterest != null && category.contains(categoryOfInterest!)) {
+ if (printExceptionNodeOnly!) {
print('$node');
} else {
print(detail);
}
}
- (groupedExceptions[category] ??= <String, int>{})
+ (groupedExceptions[category] ??= <String?, int>{})
.update(currentPackage, (value) => ++value, ifAbsent: () => 1);
++numExceptions;
}
diff --git a/sdk/lib/_internal/js_runtime/lib/js_helper.dart b/sdk/lib/_internal/js_runtime/lib/js_helper.dart
index f06c29a..cb46dfa 100644
--- a/sdk/lib/_internal/js_runtime/lib/js_helper.dart
+++ b/sdk/lib/_internal/js_runtime/lib/js_helper.dart
@@ -820,11 +820,10 @@
/// the value of the catch-all property.
static applyFunction(Function function, List? positionalArguments,
Map<String, dynamic>? namedArguments) {
- // Fast shortcut for the common case.
- if (JS('bool', '# instanceof Array', positionalArguments) &&
+ // Fast path for common cases.
+ if (positionalArguments is JSArray &&
(namedArguments == null || namedArguments.isEmpty)) {
- // Let the compiler know that we did a type-test.
- List arguments = (JS('JSArray', '#', positionalArguments));
+ JSArray arguments = positionalArguments;
int argumentCount = arguments.length;
if (argumentCount == 0) {
String selectorName = JS_GET_NAME(JsGetName.CALL_PREFIX0);
@@ -877,18 +876,17 @@
}
}
- return _genericApplyFunction2(
- function, positionalArguments, namedArguments);
+ return _generalApplyFunction(function, positionalArguments, namedArguments);
}
- static _genericApplyFunction2(Function function, List? positionalArguments,
+ static _generalApplyFunction(Function function, List? positionalArguments,
Map<String, dynamic>? namedArguments) {
List arguments;
if (positionalArguments != null) {
- if (JS('bool', '# instanceof Array', positionalArguments)) {
- arguments = JS('JSArray', '#', positionalArguments);
+ if (positionalArguments is JSArray) {
+ arguments = positionalArguments;
} else {
- arguments = new List.from(positionalArguments);
+ arguments = List.of(positionalArguments);
}
} else {
arguments = [];
@@ -934,8 +932,7 @@
return functionNoSuchMethod(function, arguments, namedArguments);
}
- bool acceptsPositionalArguments =
- JS('bool', '# instanceof Array', defaultValues);
+ bool acceptsPositionalArguments = defaultValues is JSArray;
if (acceptsPositionalArguments) {
if (namedArguments != null && namedArguments.isNotEmpty) {
@@ -950,9 +947,15 @@
// The function expects fewer arguments.
return functionNoSuchMethod(function, arguments, null);
}
- List missingDefaults = JS('JSArray', '#.slice(#)', defaultValues,
- argumentCount - requiredParameterCount);
- arguments.addAll(missingDefaults);
+ if (argumentCount < maxArguments) {
+ List missingDefaults = JS('JSArray', '#.slice(#)', defaultValues,
+ argumentCount - requiredParameterCount);
+ if (identical(arguments, positionalArguments)) {
+ // Defensive copy to avoid modifying passed-in List.
+ arguments = List.of(arguments);
+ }
+ arguments.addAll(missingDefaults);
+ }
return JS('var', '#.apply(#, #)', jsFunction, function, arguments);
} else {
// Handle named arguments.
@@ -963,6 +966,11 @@
return functionNoSuchMethod(function, arguments, namedArguments);
}
+ if (identical(arguments, positionalArguments)) {
+ // Defensive copy to avoid modifying passed-in List.
+ arguments = List.of(arguments);
+ }
+
List keys = JS('JSArray', r'Object.keys(#)', defaultValues);
if (namedArguments == null) {
for (String key in keys) {
@@ -987,6 +995,7 @@
}
}
if (used != namedArguments.length) {
+ // Named argument with name not accected by function.
return functionNoSuchMethod(function, arguments, namedArguments);
}
}
diff --git a/tests/corelib/apply6_test.dart b/tests/corelib/apply6_test.dart
new file mode 100644
index 0000000..19ee0f4d
--- /dev/null
+++ b/tests/corelib/apply6_test.dart
@@ -0,0 +1,66 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Test that the List of positional arguments to [Function.apply] is not
+// modified.
+
+import "package:expect/expect.dart";
+
+class A {
+ foo([
+ a = 10,
+ b = 20,
+ c = 30,
+ d = 40,
+ e = 50,
+ f = 60,
+ g = 70,
+ h = 80,
+ i = 90,
+ ]) =>
+ '$a $b $c $d $e $f $g $h $i';
+}
+
+String static1(a, b, {c = 30, d = 40}) => '$a $b $c $d';
+
+void test(String expected, Function function, List positional,
+ [Map<Symbol, dynamic>? named = null]) {
+ final original = List.of(positional);
+
+ Expect.equals(expected, Function.apply(function, positional, named));
+ Expect.listEquals(original, positional);
+
+ // Test again so there are multiple call sites for `Function.apply`.
+ Expect.equals(expected, Function.apply(function, positional, named));
+ Expect.listEquals(original, positional);
+}
+
+main() {
+ var a = A();
+
+ test('10 20 30 40 50 60 70 80 90', a.foo, []);
+ test('11 20 30 40 50 60 70 80 90', a.foo, [11]);
+ test('11 22 30 40 50 60 70 80 90', a.foo, [11, 22]);
+ test('11 22 33 40 50 60 70 80 90', a.foo, [11, 22, 33]);
+ test('11 22 33 44 50 60 70 80 90', a.foo, [11, 22, 33, 44]);
+ test('11 22 33 44 55 60 70 80 90', a.foo, [11, 22, 33, 44, 55]);
+ test('11 22 33 44 55 66 70 80 90', a.foo, [11, 22, 33, 44, 55, 66]);
+ test('11 22 33 44 55 66 77 80 90', a.foo, [11, 22, 33, 44, 55, 66, 77]);
+ test('11 22 33 44 55 66 77 88 90', a.foo, [11, 22, 33, 44, 55, 66, 77, 88]);
+ test('11 22 33 44 55 66 77 88 99', a.foo,
+ [11, 22, 33, 44, 55, 66, 77, 88, 99]);
+
+ // Some unmodifiable Lists. An attempt to modify the argument would fail.
+ test('11 22 33 44 55 66 77 80 90', a.foo, const [11, 22, 33, 44, 55, 66, 77]);
+ test('65 66 67 68 69 70 71 80 90', a.foo, 'ABCDEFG'.codeUnits);
+
+ test('11 22 30 40', static1, [11, 22]);
+ test('11 22 30 40', static1, [11, 22], {});
+ test('11 22 33 40', static1, [11, 22], {#c: 33});
+ test('11 22 30 44', static1, [11, 22], {#d: 44});
+ test('11 22 66 55', static1, [11, 22], {#d: 55, #c: 66});
+
+ test('11 22 88 77', static1, const [11, 22], {#d: 77, #c: 88});
+ test('65 66 11 22', static1, 'AB'.codeUnits, {#d: 22, #c: 11});
+}
diff --git a/tests/corelib_2/apply6_test.dart b/tests/corelib_2/apply6_test.dart
new file mode 100644
index 0000000..0ba08f8
--- /dev/null
+++ b/tests/corelib_2/apply6_test.dart
@@ -0,0 +1,68 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart=2.9
+
+// Test that the List of positional arguments to [Function.apply] is not
+// modified.
+
+import "package:expect/expect.dart";
+
+class A {
+ foo([
+ a = 10,
+ b = 20,
+ c = 30,
+ d = 40,
+ e = 50,
+ f = 60,
+ g = 70,
+ h = 80,
+ i = 90,
+ ]) =>
+ '$a $b $c $d $e $f $g $h $i';
+}
+
+String static1(a, b, {c = 30, d = 40}) => '$a $b $c $d';
+
+void test(String expected, Function function, List positional,
+ [Map<Symbol, dynamic> named = null]) {
+ final original = List.of(positional);
+
+ Expect.equals(expected, Function.apply(function, positional, named));
+ Expect.listEquals(original, positional);
+
+ // Test again so there are multiple call sites for `Function.apply`.
+ Expect.equals(expected, Function.apply(function, positional, named));
+ Expect.listEquals(original, positional);
+}
+
+main() {
+ var a = A();
+
+ test('10 20 30 40 50 60 70 80 90', a.foo, []);
+ test('11 20 30 40 50 60 70 80 90', a.foo, [11]);
+ test('11 22 30 40 50 60 70 80 90', a.foo, [11, 22]);
+ test('11 22 33 40 50 60 70 80 90', a.foo, [11, 22, 33]);
+ test('11 22 33 44 50 60 70 80 90', a.foo, [11, 22, 33, 44]);
+ test('11 22 33 44 55 60 70 80 90', a.foo, [11, 22, 33, 44, 55]);
+ test('11 22 33 44 55 66 70 80 90', a.foo, [11, 22, 33, 44, 55, 66]);
+ test('11 22 33 44 55 66 77 80 90', a.foo, [11, 22, 33, 44, 55, 66, 77]);
+ test('11 22 33 44 55 66 77 88 90', a.foo, [11, 22, 33, 44, 55, 66, 77, 88]);
+ test('11 22 33 44 55 66 77 88 99', a.foo,
+ [11, 22, 33, 44, 55, 66, 77, 88, 99]);
+
+ // Some unmodifiable Lists. An attempt to modify the argument would fail.
+ test('11 22 33 44 55 66 77 80 90', a.foo, const [11, 22, 33, 44, 55, 66, 77]);
+ test('65 66 67 68 69 70 71 80 90', a.foo, 'ABCDEFG'.codeUnits);
+
+ test('11 22 30 40', static1, [11, 22]);
+ test('11 22 30 40', static1, [11, 22], {});
+ test('11 22 33 40', static1, [11, 22], {#c: 33});
+ test('11 22 30 44', static1, [11, 22], {#d: 44});
+ test('11 22 66 55', static1, [11, 22], {#d: 55, #c: 66});
+
+ test('11 22 88 77', static1, const [11, 22], {#d: 77, #c: 88});
+ test('65 66 11 22', static1, 'AB'.codeUnits, {#d: 22, #c: 11});
+}
diff --git a/tools/VERSION b/tools/VERSION
index 94fff25..64e07b4 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 14
PATCH 0
-PRERELEASE 242
+PRERELEASE 243
PRERELEASE_PATCH 0
\ No newline at end of file