diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/MrjarPlugin.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/MrjarPlugin.java index 5402e0a04fe8f..b387f019ad386 100644 --- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/MrjarPlugin.java +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/MrjarPlugin.java @@ -72,18 +72,19 @@ public void apply(Project project) { var javaExtension = project.getExtensions().getByType(JavaPluginExtension.class); var isIdeaSync = System.getProperty("idea.sync.active", "false").equals("true"); var ideaSourceSetsEnabled = project.hasProperty(MRJAR_IDEA_ENABLED) && project.property(MRJAR_IDEA_ENABLED).equals("true"); + int minJavaVersion = Integer.parseInt(buildParams.getMinimumCompilerVersion().getMajorVersion()); // Ignore version-specific source sets if we are importing into IntelliJ and have not explicitly enabled this. // Avoids an IntelliJ bug: // https://youtrack.jetbrains.com/issue/IDEA-285640/Compiler-Options-Settings-language-level-is-set-incorrectly-with-JDK-19ea if (isIdeaSync == false || ideaSourceSetsEnabled) { - List mainVersions = findSourceVersions(project); + List mainVersions = findSourceVersions(project, minJavaVersion); List mainSourceSets = new ArrayList<>(); mainSourceSets.add(SourceSet.MAIN_SOURCE_SET_NAME); - configurePreviewFeatures(project, javaExtension.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME), 21); + configurePreviewFeatures(project, javaExtension.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME), minJavaVersion); List testSourceSets = new ArrayList<>(mainSourceSets); testSourceSets.add(SourceSet.TEST_SOURCE_SET_NAME); - configurePreviewFeatures(project, javaExtension.getSourceSets().getByName(SourceSet.TEST_SOURCE_SET_NAME), 21); + configurePreviewFeatures(project, javaExtension.getSourceSets().getByName(SourceSet.TEST_SOURCE_SET_NAME), minJavaVersion); for (int javaVersion : mainVersions) { String mainSourceSetName = SourceSet.MAIN_SOURCE_SET_NAME + javaVersion; SourceSet mainSourceSet = addSourceSet(project, javaExtension, mainSourceSetName, mainSourceSets, javaVersion, true); @@ -103,6 +104,7 @@ public void apply(Project project) { } private void configureMrjar(Project project) { + var jarTask = project.getTasks().withType(Jar.class).named(JavaPlugin.JAR_TASK_NAME); jarTask.configure(task -> { task.manifest(manifest -> { manifest.attributes(Map.of("Multi-Release", "true")); }); }); @@ -222,7 +224,7 @@ private void createTestTask( project.getTasks().named("check").configure(checkTask -> checkTask.dependsOn(testTaskProvider)); } - private static List findSourceVersions(Project project) { + private static List findSourceVersions(Project project, int minJavaVersion) { var srcDir = project.getProjectDir().toPath().resolve("src"); List versions = new ArrayList<>(); try (var subdirStream = Files.list(srcDir)) { @@ -231,7 +233,23 @@ private static List findSourceVersions(Project project) { String sourcesetName = sourceSetPath.getFileName().toString(); Matcher sourcesetMatcher = MRJAR_SOURCESET_PATTERN.matcher(sourcesetName); if (sourcesetMatcher.matches()) { - versions.add(Integer.parseInt(sourcesetMatcher.group(1))); + int version = Integer.parseInt(sourcesetMatcher.group(1)); + if (version < minJavaVersion) { + // NOTE: We allow mainNN for the min java version so that incubating modules can be used without warnings. + // It is a workaround for https://bugs.openjdk.org/browse/JDK-8187591. Once min java is 22, we + // can use the SuppressWarnings("preview") in the code using incubating modules and this check + // can change to <= + throw new IllegalArgumentException( + "Found src dir '" + + sourcesetName + + "' for Java " + + version + + " but multi-release jar sourceset should have version " + + minJavaVersion + + " or greater" + ); + } + versions.add(version); } } } catch (IOException e) { diff --git a/docs/changelog/120392.yaml b/docs/changelog/120392.yaml new file mode 100644 index 0000000000000..69587b4d48241 --- /dev/null +++ b/docs/changelog/120392.yaml @@ -0,0 +1,6 @@ +pr: 120392 +summary: Test/107515 restore template with match only text mapper it fail +area: Search +type: bug +issues: + - 107515 diff --git a/docs/changelog/120487.yaml b/docs/changelog/120487.yaml new file mode 100644 index 0000000000000..d728a35615156 --- /dev/null +++ b/docs/changelog/120487.yaml @@ -0,0 +1,5 @@ +pr: 120487 +summary: Fix cat_component_templates documentation +area: CAT APIs +type: bug +issues: [] diff --git a/docs/changelog/120494.yaml b/docs/changelog/120494.yaml new file mode 100644 index 0000000000000..34ba7f65e591f --- /dev/null +++ b/docs/changelog/120494.yaml @@ -0,0 +1,5 @@ +pr: 120494 +summary: Update grammar to rely on `indexPattern` instead of identifier in join target +area: ES|QL +type: enhancement +issues: [] diff --git a/docs/changelog/120617.yaml b/docs/changelog/120617.yaml new file mode 100644 index 0000000000000..cdf93ef4e71f2 --- /dev/null +++ b/docs/changelog/120617.yaml @@ -0,0 +1,5 @@ +pr: 120617 +summary: Fix queries with document level security on lookup indexes +area: ES|QL +type: bug +issues: [120509] diff --git a/docs/changelog/120717.yaml b/docs/changelog/120717.yaml new file mode 100644 index 0000000000000..c5609e7e3df5f --- /dev/null +++ b/docs/changelog/120717.yaml @@ -0,0 +1,6 @@ +pr: 120717 +summary: Fix LTR rescorer throws 'local model reference is null' on multi-shards index when explained is enabled +area: Ranking +type: bug +issues: + - 120739 diff --git a/docs/changelog/120748.yaml b/docs/changelog/120748.yaml new file mode 100644 index 0000000000000..e2ec312f189b0 --- /dev/null +++ b/docs/changelog/120748.yaml @@ -0,0 +1,15 @@ +pr: 120748 +summary: Removing support for types field in watcher search +area: Watcher +type: breaking +issues: [] +breaking: + title: Removing support for types field in watcher search + area: REST API + details: >- + Previously, setting the `input.search.request.types` field in the payload when creating a watcher to an empty array + was allowed, although it resulted in a deprecation warning and had no effect (and any value other than an empty + array would result in an error). Now, support for this field is entirely removed, and the empty array will also + result in an error. + impact: Users should stop setting this field (which did not have any effect anyway). + notable: false diff --git a/docs/changelog/120752.yaml b/docs/changelog/120752.yaml new file mode 100644 index 0000000000000..674d2190244b1 --- /dev/null +++ b/docs/changelog/120752.yaml @@ -0,0 +1,6 @@ +pr: 120752 +summary: Refresh source index before reindexing data stream index +area: Data streams +type: bug +issues: + - 120314 diff --git a/docs/changelog/120753.yaml b/docs/changelog/120753.yaml new file mode 100644 index 0000000000000..4885ab4be9add --- /dev/null +++ b/docs/changelog/120753.yaml @@ -0,0 +1,5 @@ +pr: 120753 +summary: Optimize `IngestDocMetadata` `isAvailable` +area: Ingest Node +type: enhancement +issues: [] diff --git a/docs/changelog/120781.yaml b/docs/changelog/120781.yaml new file mode 100644 index 0000000000000..67c7d90528d6e --- /dev/null +++ b/docs/changelog/120781.yaml @@ -0,0 +1,5 @@ +pr: 120781 +summary: Add back `keep_alive` to `async_search.submit` rest-api-spec +area: Search +type: bug +issues: [] diff --git a/docs/changelog/120809.yaml b/docs/changelog/120809.yaml new file mode 100644 index 0000000000000..30a3736dc93a4 --- /dev/null +++ b/docs/changelog/120809.yaml @@ -0,0 +1,6 @@ +pr: 120809 +summary: LTR sometines throw `NullPointerException:` Cannot read field "approximation" + because "top" is null +area: Ranking +type: bug +issues: [] diff --git a/docs/reference/indices/index-templates.asciidoc b/docs/reference/indices/index-templates.asciidoc index 90c4a6952446e..b13921d263f71 100644 --- a/docs/reference/indices/index-templates.asciidoc +++ b/docs/reference/indices/index-templates.asciidoc @@ -40,6 +40,7 @@ template with the highest priority is used. following index patterns: // tag::built-in-index-template-patterns[] +- `.kibana-reporting*` - `logs-*-*` - `metrics-*-*` - `synthetics-*-*` diff --git a/docs/reference/indices/put-component-template.asciidoc b/docs/reference/indices/put-component-template.asciidoc index 9f129c3507d87..ccdafaf2fd050 100644 --- a/docs/reference/indices/put-component-template.asciidoc +++ b/docs/reference/indices/put-component-template.asciidoc @@ -97,6 +97,7 @@ Name of the component template to create. {es} includes the following built-in component templates: // tag::built-in-component-templates[] +- `kibana-reporting@settings` - `logs@mappings` - `logs@settings` - `metrics@mappings` diff --git a/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/VersionSpecificNetworkChecks.java b/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/VersionSpecificNetworkChecks.java index 548bce8e2f766..d94597c2d9dd0 100644 --- a/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/VersionSpecificNetworkChecks.java +++ b/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/VersionSpecificNetworkChecks.java @@ -14,21 +14,51 @@ import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; +import java.net.spi.InetAddressResolver; +import java.net.spi.InetAddressResolverProvider; class VersionSpecificNetworkChecks { - static void createInetAddressResolverProvider() {} + static void createInetAddressResolverProvider() { + var x = new InetAddressResolverProvider() { + @Override + public InetAddressResolver get(Configuration configuration) { + return null; + } + + @Override + public String name() { + return "TEST"; + } + }; + } static void httpClientSend() throws InterruptedException { - HttpClient httpClient = HttpClient.newBuilder().build(); - try { - httpClient.send(HttpRequest.newBuilder(URI.create("http://localhost")).build(), HttpResponse.BodyHandlers.discarding()); - } catch (IOException e) { - // Expected, the send action may fail with these parameters (but after it run the entitlement check in the prologue) + try (HttpClient httpClient = HttpClient.newBuilder().build()) { + // Shutdown the client, so the send action will shortcut before actually executing any network operation + // (but after it run our check in the prologue) + httpClient.shutdown(); + try { + httpClient.send(HttpRequest.newBuilder(URI.create("http://localhost")).build(), HttpResponse.BodyHandlers.discarding()); + } catch (IOException e) { + // Expected, since we shut down the client + } } } static void httpClientSendAsync() { - HttpClient httpClient = HttpClient.newBuilder().build(); - httpClient.sendAsync(HttpRequest.newBuilder(URI.create("http://localhost")).build(), HttpResponse.BodyHandlers.discarding()); + try (HttpClient httpClient = HttpClient.newBuilder().build()) { + // Shutdown the client, so the send action will return before actually executing any network operation + // (but after it run our check in the prologue) + httpClient.shutdown(); + var future = httpClient.sendAsync( + HttpRequest.newBuilder(URI.create("http://localhost")).build(), + HttpResponse.BodyHandlers.discarding() + ); + assert future.isCompletedExceptionally(); + future.exceptionally(ex -> { + assert ex instanceof IOException; + return null; + }); + } } } diff --git a/libs/entitlement/qa/entitlement-test-plugin/src/main18/java/org/elasticsearch/entitlement/qa/test/VersionSpecificNetworkChecks.java b/libs/entitlement/qa/entitlement-test-plugin/src/main18/java/org/elasticsearch/entitlement/qa/test/VersionSpecificNetworkChecks.java deleted file mode 100644 index 5a456c65d8206..0000000000000 --- a/libs/entitlement/qa/entitlement-test-plugin/src/main18/java/org/elasticsearch/entitlement/qa/test/VersionSpecificNetworkChecks.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -package org.elasticsearch.entitlement.qa.test; - -import java.io.IOException; -import java.net.URI; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; -import java.net.spi.InetAddressResolver; -import java.net.spi.InetAddressResolverProvider; - -class VersionSpecificNetworkChecks { - static void createInetAddressResolverProvider() { - var x = new InetAddressResolverProvider() { - @Override - public InetAddressResolver get(Configuration configuration) { - return null; - } - - @Override - public String name() { - return "TEST"; - } - }; - } - - static void httpClientSend() throws InterruptedException { - HttpClient httpClient = HttpClient.newBuilder().build(); - try { - httpClient.send(HttpRequest.newBuilder(URI.create("http://localhost")).build(), HttpResponse.BodyHandlers.discarding()); - } catch (IOException e) { - // Expected, the send action may fail with these parameters (but after it run the entitlement check in the prologue) - } - } - - static void httpClientSendAsync() { - HttpClient httpClient = HttpClient.newBuilder().build(); - httpClient.sendAsync(HttpRequest.newBuilder(URI.create("http://localhost")).build(), HttpResponse.BodyHandlers.discarding()); - } -} diff --git a/libs/entitlement/qa/entitlement-test-plugin/src/main21/java/org/elasticsearch/entitlement/qa/test/VersionSpecificNetworkChecks.java b/libs/entitlement/qa/entitlement-test-plugin/src/main21/java/org/elasticsearch/entitlement/qa/test/VersionSpecificNetworkChecks.java deleted file mode 100644 index d94597c2d9dd0..0000000000000 --- a/libs/entitlement/qa/entitlement-test-plugin/src/main21/java/org/elasticsearch/entitlement/qa/test/VersionSpecificNetworkChecks.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -package org.elasticsearch.entitlement.qa.test; - -import java.io.IOException; -import java.net.URI; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; -import java.net.spi.InetAddressResolver; -import java.net.spi.InetAddressResolverProvider; - -class VersionSpecificNetworkChecks { - static void createInetAddressResolverProvider() { - var x = new InetAddressResolverProvider() { - @Override - public InetAddressResolver get(Configuration configuration) { - return null; - } - - @Override - public String name() { - return "TEST"; - } - }; - } - - static void httpClientSend() throws InterruptedException { - try (HttpClient httpClient = HttpClient.newBuilder().build()) { - // Shutdown the client, so the send action will shortcut before actually executing any network operation - // (but after it run our check in the prologue) - httpClient.shutdown(); - try { - httpClient.send(HttpRequest.newBuilder(URI.create("http://localhost")).build(), HttpResponse.BodyHandlers.discarding()); - } catch (IOException e) { - // Expected, since we shut down the client - } - } - } - - static void httpClientSendAsync() { - try (HttpClient httpClient = HttpClient.newBuilder().build()) { - // Shutdown the client, so the send action will return before actually executing any network operation - // (but after it run our check in the prologue) - httpClient.shutdown(); - var future = httpClient.sendAsync( - HttpRequest.newBuilder(URI.create("http://localhost")).build(), - HttpResponse.BodyHandlers.discarding() - ); - assert future.isCompletedExceptionally(); - future.exceptionally(ex -> { - assert ex instanceof IOException; - return null; - }); - } - } -} diff --git a/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/AbstractEntitlementsIT.java b/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/AbstractEntitlementsIT.java new file mode 100644 index 0000000000000..b770b4915a317 --- /dev/null +++ b/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/AbstractEntitlementsIT.java @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +package org.elasticsearch.entitlement.qa; + +import org.elasticsearch.client.Request; +import org.elasticsearch.client.Response; +import org.elasticsearch.test.rest.ESRestTestCase; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; + +public abstract class AbstractEntitlementsIT extends ESRestTestCase { + + static final EntitlementsTestRule.PolicyBuilder ALLOWED_TEST_ENTITLEMENTS = (builder, tempDir) -> { + builder.value("create_class_loader"); + builder.value("set_https_connection_properties"); + builder.value("inbound_network"); + builder.value("outbound_network"); + builder.value("load_native_libraries"); + builder.value( + Map.of( + "write_system_properties", + Map.of("properties", List.of("es.entitlements.checkSetSystemProperty", "es.entitlements.checkClearSystemProperty")) + ) + ); + }; + + private final String actionName; + private final boolean expectAllowed; + + AbstractEntitlementsIT(String actionName, boolean expectAllowed) { + this.actionName = actionName; + this.expectAllowed = expectAllowed; + } + + private Response executeCheck() throws IOException { + var request = new Request("GET", "/_entitlement_check"); + request.addParameter("action", actionName); + return client().performRequest(request); + } + + public void testAction() throws IOException { + logger.info("Executing Entitlement test for [{}]", actionName); + if (expectAllowed) { + Response result = executeCheck(); + assertThat(result.getStatusLine().getStatusCode(), equalTo(200)); + } else { + var exception = expectThrows(IOException.class, this::executeCheck); + assertThat(exception.getMessage(), containsString("not_entitled_exception")); + } + } +} diff --git a/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsAllowedIT.java b/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsAllowedIT.java index 54628fc674d75..159083c2dd6ba 100644 --- a/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsAllowedIT.java +++ b/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsAllowedIT.java @@ -12,31 +12,16 @@ import com.carrotsearch.randomizedtesting.annotations.Name; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; -import org.elasticsearch.client.Request; -import org.elasticsearch.client.Response; import org.elasticsearch.entitlement.qa.test.RestEntitlementsCheckAction; -import org.elasticsearch.test.cluster.ElasticsearchCluster; -import org.elasticsearch.test.rest.ESRestTestCase; import org.junit.ClassRule; -import java.io.IOException; - -import static org.elasticsearch.entitlement.qa.EntitlementsUtil.ALLOWED_ENTITLEMENTS; -import static org.hamcrest.Matchers.equalTo; - -public class EntitlementsAllowedIT extends ESRestTestCase { +public class EntitlementsAllowedIT extends AbstractEntitlementsIT { @ClassRule - public static ElasticsearchCluster cluster = ElasticsearchCluster.local() - .module("entitlement-test-plugin", spec -> EntitlementsUtil.setupEntitlements(spec, true, ALLOWED_ENTITLEMENTS)) - .systemProperty("es.entitlements.enabled", "true") - .setting("xpack.security.enabled", "false") - .build(); - - private final String actionName; + public static EntitlementsTestRule testRule = new EntitlementsTestRule(true, ALLOWED_TEST_ENTITLEMENTS); public EntitlementsAllowedIT(@Name("actionName") String actionName) { - this.actionName = actionName; + super(actionName, true); } @ParametersFactory @@ -46,14 +31,6 @@ public static Iterable data() { @Override protected String getTestRestCluster() { - return cluster.getHttpAddresses(); - } - - public void testCheckActionWithPolicyPass() throws IOException { - logger.info("Executing Entitlement test for [{}]", actionName); - var request = new Request("GET", "/_entitlement_check"); - request.addParameter("action", actionName); - Response result = client().performRequest(request); - assertThat(result.getStatusLine().getStatusCode(), equalTo(200)); + return testRule.cluster.getHttpAddresses(); } } diff --git a/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsAllowedNonModularIT.java b/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsAllowedNonModularIT.java index 8390f0e5fd115..c99a05ff57ece 100644 --- a/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsAllowedNonModularIT.java +++ b/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsAllowedNonModularIT.java @@ -12,31 +12,16 @@ import com.carrotsearch.randomizedtesting.annotations.Name; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; -import org.elasticsearch.client.Request; -import org.elasticsearch.client.Response; import org.elasticsearch.entitlement.qa.test.RestEntitlementsCheckAction; -import org.elasticsearch.test.cluster.ElasticsearchCluster; -import org.elasticsearch.test.rest.ESRestTestCase; import org.junit.ClassRule; -import java.io.IOException; - -import static org.elasticsearch.entitlement.qa.EntitlementsUtil.ALLOWED_ENTITLEMENTS; -import static org.hamcrest.Matchers.equalTo; - -public class EntitlementsAllowedNonModularIT extends ESRestTestCase { +public class EntitlementsAllowedNonModularIT extends AbstractEntitlementsIT { @ClassRule - public static ElasticsearchCluster cluster = ElasticsearchCluster.local() - .module("entitlement-test-plugin", spec -> EntitlementsUtil.setupEntitlements(spec, false, ALLOWED_ENTITLEMENTS)) - .systemProperty("es.entitlements.enabled", "true") - .setting("xpack.security.enabled", "false") - .build(); - - private final String actionName; + public static EntitlementsTestRule testRule = new EntitlementsTestRule(false, ALLOWED_TEST_ENTITLEMENTS); public EntitlementsAllowedNonModularIT(@Name("actionName") String actionName) { - this.actionName = actionName; + super(actionName, true); } @ParametersFactory @@ -46,14 +31,6 @@ public static Iterable data() { @Override protected String getTestRestCluster() { - return cluster.getHttpAddresses(); - } - - public void testCheckActionWithPolicyPass() throws IOException { - logger.info("Executing Entitlement test for [{}]", actionName); - var request = new Request("GET", "/_entitlement_check"); - request.addParameter("action", actionName); - Response result = client().performRequest(request); - assertThat(result.getStatusLine().getStatusCode(), equalTo(200)); + return testRule.cluster.getHttpAddresses(); } } diff --git a/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsDeniedIT.java b/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsDeniedIT.java index 3405e41897cc1..6f348d38d8e53 100644 --- a/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsDeniedIT.java +++ b/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsDeniedIT.java @@ -12,36 +12,16 @@ import com.carrotsearch.randomizedtesting.annotations.Name; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; -import org.elasticsearch.client.Request; import org.elasticsearch.entitlement.qa.test.RestEntitlementsCheckAction; -import org.elasticsearch.test.cluster.ElasticsearchCluster; -import org.elasticsearch.test.rest.ESRestTestCase; import org.junit.ClassRule; -import java.io.IOException; - -import static org.hamcrest.Matchers.containsString; - -public class EntitlementsDeniedIT extends ESRestTestCase { +public class EntitlementsDeniedIT extends AbstractEntitlementsIT { @ClassRule - public static ElasticsearchCluster cluster = ElasticsearchCluster.local() - .module("entitlement-test-plugin", spec -> EntitlementsUtil.setupEntitlements(spec, true, null)) - .systemProperty("es.entitlements.enabled", "true") - .setting("xpack.security.enabled", "false") - // Logs in libs/entitlement/qa/build/test-results/javaRestTest/TEST-org.elasticsearch.entitlement.qa.EntitlementsDeniedIT.xml - // .setting("logger.org.elasticsearch.entitlement", "DEBUG") - .build(); - - @Override - protected String getTestRestCluster() { - return cluster.getHttpAddresses(); - } - - private final String actionName; + public static EntitlementsTestRule testRule = new EntitlementsTestRule(true, null); public EntitlementsDeniedIT(@Name("actionName") String actionName) { - this.actionName = actionName; + super(actionName, false); } @ParametersFactory @@ -49,13 +29,8 @@ public static Iterable data() { return RestEntitlementsCheckAction.getAllCheckActions().stream().map(action -> new Object[] { action }).toList(); } - public void testCheckThrows() { - logger.info("Executing Entitlement test for [{}]", actionName); - var exception = expectThrows(IOException.class, () -> { - var request = new Request("GET", "/_entitlement_check"); - request.addParameter("action", actionName); - client().performRequest(request); - }); - assertThat(exception.getMessage(), containsString("not_entitled_exception")); + @Override + protected String getTestRestCluster() { + return testRule.cluster.getHttpAddresses(); } } diff --git a/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsDeniedNonModularIT.java b/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsDeniedNonModularIT.java index a2a4773bf7523..6f2003f7275d4 100644 --- a/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsDeniedNonModularIT.java +++ b/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsDeniedNonModularIT.java @@ -12,36 +12,16 @@ import com.carrotsearch.randomizedtesting.annotations.Name; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; -import org.elasticsearch.client.Request; import org.elasticsearch.entitlement.qa.test.RestEntitlementsCheckAction; -import org.elasticsearch.test.cluster.ElasticsearchCluster; -import org.elasticsearch.test.rest.ESRestTestCase; import org.junit.ClassRule; -import java.io.IOException; - -import static org.hamcrest.Matchers.containsString; - -public class EntitlementsDeniedNonModularIT extends ESRestTestCase { +public class EntitlementsDeniedNonModularIT extends AbstractEntitlementsIT { @ClassRule - public static ElasticsearchCluster cluster = ElasticsearchCluster.local() - .module("entitlement-test-plugin", spec -> EntitlementsUtil.setupEntitlements(spec, false, null)) - .systemProperty("es.entitlements.enabled", "true") - .setting("xpack.security.enabled", "false") - // Logs in libs/entitlement/qa/build/test-results/javaRestTest/TEST-org.elasticsearch.entitlement.qa.EntitlementsDeniedIT.xml - // .setting("logger.org.elasticsearch.entitlement", "DEBUG") - .build(); - - @Override - protected String getTestRestCluster() { - return cluster.getHttpAddresses(); - } - - private final String actionName; + public static EntitlementsTestRule testRule = new EntitlementsTestRule(false, null); public EntitlementsDeniedNonModularIT(@Name("actionName") String actionName) { - this.actionName = actionName; + super(actionName, false); } @ParametersFactory @@ -49,13 +29,8 @@ public static Iterable data() { return RestEntitlementsCheckAction.getAllCheckActions().stream().map(action -> new Object[] { action }).toList(); } - public void testCheckThrows() { - logger.info("Executing Entitlement test for [{}]", actionName); - var exception = expectThrows(IOException.class, () -> { - var request = new Request("GET", "/_entitlement_check"); - request.addParameter("action", actionName); - client().performRequest(request); - }); - assertThat(exception.getMessage(), containsString("not_entitled_exception")); + @Override + protected String getTestRestCluster() { + return testRule.cluster.getHttpAddresses(); } } diff --git a/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsTestRule.java b/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsTestRule.java new file mode 100644 index 0000000000000..1a0a75588f02c --- /dev/null +++ b/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsTestRule.java @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +package org.elasticsearch.entitlement.qa; + +import org.elasticsearch.common.Strings; +import org.elasticsearch.test.cluster.ElasticsearchCluster; +import org.elasticsearch.test.cluster.local.PluginInstallSpec; +import org.elasticsearch.test.cluster.util.resource.Resource; +import org.elasticsearch.xcontent.XContentBuilder; +import org.elasticsearch.xcontent.yaml.YamlXContent; +import org.junit.rules.RuleChain; +import org.junit.rules.TemporaryFolder; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Path; + +class EntitlementsTestRule implements TestRule { + + interface PolicyBuilder { + void build(XContentBuilder builder, Path tempDir) throws IOException; + } + + final TemporaryFolder testDir; + final ElasticsearchCluster cluster; + final TestRule ruleChain; + + @SuppressWarnings("this-escape") + EntitlementsTestRule(boolean modular, PolicyBuilder policyBuilder) { + testDir = new TemporaryFolder(); + cluster = ElasticsearchCluster.local() + .module("entitlement-test-plugin", spec -> setupEntitlements(spec, modular, policyBuilder)) + .systemProperty("es.entitlements.enabled", "true") + .systemProperty("es.entitlements.testdir", () -> testDir.getRoot().getAbsolutePath()) + .setting("xpack.security.enabled", "false") + .build(); + ruleChain = RuleChain.outerRule(testDir).around(cluster); + } + + @Override + public Statement apply(Statement statement, Description description) { + return ruleChain.apply(statement, description); + } + + private void setupEntitlements(PluginInstallSpec spec, boolean modular, PolicyBuilder policyBuilder) { + String moduleName = modular ? "org.elasticsearch.entitlement.qa.test" : "ALL-UNNAMED"; + if (policyBuilder != null) { + spec.withEntitlementsOverride(old -> { + try { + try (var builder = YamlXContent.contentBuilder()) { + builder.startObject(); + builder.field(moduleName); + builder.startArray(); + policyBuilder.build(builder, testDir.getRoot().toPath()); + builder.endArray(); + builder.endObject(); + + String policy = Strings.toString(builder); + System.out.println("Using entitlement policy:\n" + policy); + return Resource.fromString(policy); + } + + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + } + + if (modular == false) { + spec.withPropertiesOverride(old -> { + String props = old.replace("modulename=org.elasticsearch.entitlement.qa.test", ""); + System.out.println("Using plugin properties:\n" + props); + return Resource.fromString(props); + }); + } + } +} diff --git a/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsUtil.java b/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsUtil.java deleted file mode 100644 index 46d411baf1aa0..0000000000000 --- a/libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsUtil.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -package org.elasticsearch.entitlement.qa; - -import org.elasticsearch.common.Strings; -import org.elasticsearch.core.CheckedConsumer; -import org.elasticsearch.test.cluster.local.PluginInstallSpec; -import org.elasticsearch.test.cluster.util.resource.Resource; -import org.elasticsearch.xcontent.XContentBuilder; -import org.elasticsearch.xcontent.yaml.YamlXContent; - -import java.io.IOException; -import java.io.UncheckedIOException; -import java.util.List; -import java.util.Map; - -class EntitlementsUtil { - - static final CheckedConsumer ALLOWED_ENTITLEMENTS = builder -> { - builder.value("create_class_loader"); - builder.value("set_https_connection_properties"); - builder.value("inbound_network"); - builder.value("outbound_network"); - builder.value("load_native_libraries"); - builder.value( - Map.of( - "write_system_properties", - Map.of("properties", List.of("es.entitlements.checkSetSystemProperty", "es.entitlements.checkClearSystemProperty")) - ) - ); - }; - - static void setupEntitlements(PluginInstallSpec spec, boolean modular, CheckedConsumer policyBuilder) { - String moduleName = modular ? "org.elasticsearch.entitlement.qa.test" : "ALL-UNNAMED"; - if (policyBuilder != null) { - try { - try (var builder = YamlXContent.contentBuilder()) { - builder.startObject(); - builder.field(moduleName); - builder.startArray(); - policyBuilder.accept(builder); - builder.endArray(); - builder.endObject(); - - String policy = Strings.toString(builder); - System.out.println("Using entitlement policy:\n" + policy); - spec.withEntitlementsOverride(old -> Resource.fromString(policy)); - } - - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - if (modular == false) { - spec.withPropertiesOverride(old -> { - String props = old.replace("modulename=org.elasticsearch.entitlement.qa.test", ""); - System.out.println("Using plugin properties:\n" + props); - return Resource.fromString(props); - }); - } - } - - private EntitlementsUtil() {} -} diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/APMAgentSettings.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/APMAgentSettings.java index 9d4822aa9c4d6..68adc97b74449 100644 --- a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/APMAgentSettings.java +++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/APMAgentSettings.java @@ -90,6 +90,11 @@ public void initAgentSystemProperties(Settings settings) { */ @SuppressForbidden(reason = "Need to be able to manipulate APM agent-related properties to set them dynamically") public void setAgentSetting(String key, String value) { + if (key.startsWith("global_labels.")) { + // Invalid agent setting, leftover from flattening global labels in APMJVMOptions + // https://github.com/elastic/elasticsearch/issues/120791 + return; + } final String completeKey = "elastic.apm." + Objects.requireNonNull(key); AccessController.doPrivileged((PrivilegedAction) () -> { if (value == null || value.isEmpty()) { @@ -242,8 +247,8 @@ private static Setting concreteAgentSetting(String namespace, String qua return new Setting<>(qualifiedKey, "", (value) -> { if (qualifiedKey.equals("_na_") == false && PERMITTED_AGENT_KEYS.contains(namespace) == false) { if (namespace.startsWith("global_labels.")) { - // The nested labels syntax is transformed in APMJvmOptions. - // Ignore these here to not fail if not correctly removed. + // Invalid agent setting, leftover from flattening global labels in APMJVMOptions + // https://github.com/elastic/elasticsearch/issues/120791 return value; } throw new IllegalArgumentException("Configuration [" + qualifiedKey + "] is either prohibited or unknown."); diff --git a/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamsSnapshotsIT.java b/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamsSnapshotsIT.java index e78d9b4f2b8cf..53d75454339a9 100644 --- a/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamsSnapshotsIT.java +++ b/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamsSnapshotsIT.java @@ -1137,11 +1137,8 @@ public void testPartialRestoreSnapshotThatIncludesDataStream() { /** * This test is a copy of the {@link #testPartialRestoreSnapshotThatIncludesDataStream()} the only difference - * is that one include the global state and one doesn't. In general this shouldn't matter that's why it used to be - * a random parameter of the test, but because of #107515 it fails when we include the global state. Keep them - * separate until this is fixed. + * is that one include the global state and one doesn't. */ - @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/107515") public void testPartialRestoreSnapshotThatIncludesDataStreamWithGlobalState() { final String snapshot = "test-snapshot"; final String indexWithoutDataStream = "test-idx-no-ds"; @@ -1307,11 +1304,8 @@ public void testExcludeDSFromSnapshotWhenExcludingAnyOfItsIndices() { /** * This test is a copy of the {@link #testExcludeDSFromSnapshotWhenExcludingAnyOfItsIndices()} ()} the only difference - * is that one include the global state and one doesn't. In general this shouldn't matter that's why it used to be - * a random parameter of the test, but because of #107515 it fails when we include the global state. Keep them - * separate until this is fixed. + * is that one include the global state and one doesn't. */ - @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/107515") public void testExcludeDSFromSnapshotWhenExcludingItsIndicesWithGlobalState() { final String snapshot = "test-snapshot"; final String indexWithoutDataStream = "test-idx-no-ds"; @@ -1477,10 +1471,6 @@ public void testWarningHeaderAbsentOnRestoreWithTemplates() throws Exception { } - /** - * This test is muted as it's awaiting the same fix as {@link #testPartialRestoreSnapshotThatIncludesDataStreamWithGlobalState()} - */ - @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/107515") public void testWarningHeaderOnRestoreTemplateFromSnapshot() throws Exception { String datastreamName = "ds"; diff --git a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/RenameProcessorTests.java b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/RenameProcessorTests.java index 1ebc5f16a65d3..bc7caf93b0036 100644 --- a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/RenameProcessorTests.java +++ b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/RenameProcessorTests.java @@ -14,7 +14,6 @@ import org.elasticsearch.ingest.RandomDocumentPicks; import org.elasticsearch.ingest.TestIngestDocument; import org.elasticsearch.ingest.TestTemplateService; -import org.elasticsearch.script.Metadata; import org.elasticsearch.test.ESTestCase; import java.util.ArrayList; @@ -140,42 +139,40 @@ public void testRenameExistingFieldNullValue() throws Exception { public void testRenameAtomicOperationSetFails() throws Exception { Map metadata = new HashMap<>(); - metadata.put("list", List.of("item")); - - IngestDocument ingestDocument = TestIngestDocument.ofMetadataWithValidator( - metadata, - Map.of("new_field", new Metadata.FieldProperty<>(Object.class, true, true, (k, v) -> { - if (v != null) { - throw new UnsupportedOperationException(); - } - }), "list", new Metadata.FieldProperty<>(Object.class, true, true, null)) - ); - Processor processor = createRenameProcessor("list", "new_field", false, false); + metadata.put("_index", "foobar"); + + IngestDocument ingestDocument = TestIngestDocument.withDefaultVersion(metadata); + Processor processor = createRenameProcessor("_index", "_version_type", false, false); try { processor.execute(ingestDocument); fail("processor execute should have failed"); - } catch (UnsupportedOperationException e) { + } catch (IllegalArgumentException e) { // the set failed, the old field has not been removed - assertThat(ingestDocument.getSourceAndMetadata().containsKey("list"), equalTo(true)); - assertThat(ingestDocument.getSourceAndMetadata().containsKey("new_field"), equalTo(false)); + assertThat( + e.getMessage(), + equalTo( + "_version_type must be a null or one of [internal, external, external_gte] " + + "but was [foobar] with type [java.lang.String]" + ) + ); + assertThat(ingestDocument.getSourceAndMetadata().containsKey("_index"), equalTo(true)); + assertThat(ingestDocument.getSourceAndMetadata().containsKey("_version_type"), equalTo(false)); } } public void testRenameAtomicOperationRemoveFails() throws Exception { Map metadata = new HashMap<>(); - metadata.put("list", List.of("item")); + metadata.put("foo", "bar"); - IngestDocument ingestDocument = TestIngestDocument.ofMetadataWithValidator( - metadata, - Map.of("list", new Metadata.FieldProperty<>(Object.class, false, true, null)) - ); - Processor processor = createRenameProcessor("list", "new_field", false, false); + IngestDocument ingestDocument = TestIngestDocument.withDefaultVersion(metadata); + Processor processor = createRenameProcessor("_version", "new_field", false, false); try { processor.execute(ingestDocument); fail("processor execute should have failed"); } catch (IllegalArgumentException e) { - // the set failed, the old field has not been removed - assertThat(ingestDocument.getSourceAndMetadata().containsKey("list"), equalTo(true)); + // the remove failed, the old field has not been removed + assertThat(e.getMessage(), equalTo("_version cannot be removed")); + assertThat(ingestDocument.getSourceAndMetadata().containsKey("_version"), equalTo(true)); assertThat(ingestDocument.getSourceAndMetadata().containsKey("new_field"), equalTo(false)); } } diff --git a/muted-tests.yml b/muted-tests.yml index 596001b5aac1a..01d935b7340b2 100644 --- a/muted-tests.yml +++ b/muted-tests.yml @@ -131,9 +131,6 @@ tests: - class: org.elasticsearch.xpack.ml.integration.RegressionIT method: testTwoJobsWithSameRandomizeSeedUseSameTrainingSet issue: https://github.com/elastic/elasticsearch/issues/117805 -- class: org.elasticsearch.xpack.remotecluster.CrossClusterEsqlRCS2UnavailableRemotesIT - method: testEsqlRcs2UnavailableRemoteScenarios - issue: https://github.com/elastic/elasticsearch/issues/117419 - class: org.elasticsearch.xpack.esql.action.EsqlActionTaskIT method: testCancelRequestWhenFailingFetchingPages issue: https://github.com/elastic/elasticsearch/issues/118193 @@ -149,9 +146,6 @@ tests: - class: org.elasticsearch.action.search.SearchQueryThenFetchAsyncActionTests method: testBottomFieldSort issue: https://github.com/elastic/elasticsearch/issues/118214 -- class: org.elasticsearch.xpack.remotecluster.CrossClusterEsqlRCS1UnavailableRemotesIT - method: testEsqlRcs1UnavailableRemoteScenarios - issue: https://github.com/elastic/elasticsearch/issues/118350 - class: org.elasticsearch.xpack.searchablesnapshots.RetrySearchIntegTests method: testSearcherId issue: https://github.com/elastic/elasticsearch/issues/118374 @@ -205,9 +199,6 @@ tests: - class: org.elasticsearch.oldrepos.OldRepositoryAccessIT method: testOldSourceOnlyRepoAccess issue: https://github.com/elastic/elasticsearch/issues/120080 -- class: org.elasticsearch.xpack.migrate.action.ReindexDatastreamIndexTransportActionIT - method: testTsdbStartEndSet - issue: https://github.com/elastic/elasticsearch/issues/120314 - class: org.elasticsearch.xpack.test.rest.XPackRestIT method: test {p0=snapshot/10_basic/Failed to snapshot indices with synthetic source} issue: https://github.com/elastic/elasticsearch/issues/120332 @@ -246,6 +237,14 @@ tests: - class: org.elasticsearch.action.search.SearchProgressActionListenerIT method: testSearchProgressWithHits issue: https://github.com/elastic/elasticsearch/issues/120671 +- class: org.elasticsearch.xpack.ml.integration.LearningToRankExplainIT + method: testLtrExplainWithMultipleShardsAndReplicas + issue: https://github.com/elastic/elasticsearch/issues/120805 +- class: org.elasticsearch.xpack.test.rest.XPackRestIT + method: test {p0=ml/3rd_party_deployment/Test start deployment fails while model download in progress} + issue: https://github.com/elastic/elasticsearch/issues/120810 +- class: org.elasticsearch.indices.mapping.UpdateMappingIntegrationIT + issue: https://github.com/elastic/elasticsearch/issues/116126 # Examples: # diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/async_search.submit.json b/rest-api-spec/src/main/resources/rest-api-spec/api/async_search.submit.json index 3de0dec85f547..8ae2fff22281c 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/async_search.submit.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/async_search.submit.json @@ -43,6 +43,11 @@ "description":"Control whether the response should be stored in the cluster if it completed within the provided [wait_for_completion] time (default: false)", "default":false }, + "keep_alive": { + "type": "time", + "description": "Update the time interval in which the results (partial or final) for this search will be available", + "default": "5d" + }, "batched_reduce_size":{ "type":"number", "description":"The number of shard results that should be reduced at once on the coordinating node. This value should be used as the granularity at which progress results will be made available.", diff --git a/server/src/main/java/org/elasticsearch/ReleaseVersions.java b/server/src/main/java/org/elasticsearch/ReleaseVersions.java index 09d6bdd1b4799..22cd18c7b4ac3 100644 --- a/server/src/main/java/org/elasticsearch/ReleaseVersions.java +++ b/server/src/main/java/org/elasticsearch/ReleaseVersions.java @@ -10,7 +10,7 @@ package org.elasticsearch; import org.elasticsearch.common.Strings; -import org.elasticsearch.core.UpdateForV9; +import org.elasticsearch.core.UpdateForV10; import org.elasticsearch.internal.BuildExtension; import org.elasticsearch.plugins.ExtensionLoader; @@ -114,8 +114,7 @@ private static IntFunction lookupFunction(NavigableMap, ToXContentFragment { VERSION_STRINGS = Map.copyOf(builderByString); } - @UpdateForV9(owner = UpdateForV9.Owner.CORE_INFRA) - // Re-enable this assertion once the rest api version is bumped private static void assertRestApiVersion() { - // assert RestApiVersion.current().major == CURRENT.major && RestApiVersion.previous().major == CURRENT.major - 1 - // : "RestApiVersion must be upgraded " - // + "to reflect major from Version.CURRENT [" - // + CURRENT.major - // + "]" - // + " but is still set to [" - // + RestApiVersion.current().major - // + "]"; + assert RestApiVersion.current().major == CURRENT.major && RestApiVersion.previous().major == CURRENT.major - 1 + : "RestApiVersion must be upgraded " + + "to reflect major from Version.CURRENT [" + + CURRENT.major + + "]" + + " but is still set to [" + + RestApiVersion.current().major + + "]"; } public static Version readVersion(StreamInput in) throws IOException { diff --git a/server/src/main/java/org/elasticsearch/cluster/ClusterState.java b/server/src/main/java/org/elasticsearch/cluster/ClusterState.java index 62f2947d06a41..6b222fb8f5bdc 100644 --- a/server/src/main/java/org/elasticsearch/cluster/ClusterState.java +++ b/server/src/main/java/org/elasticsearch/cluster/ClusterState.java @@ -47,7 +47,6 @@ import org.elasticsearch.common.xcontent.ChunkedToXContentHelper; import org.elasticsearch.core.Nullable; import org.elasticsearch.core.SuppressForbidden; -import org.elasticsearch.core.UpdateForV9; import org.elasticsearch.index.shard.IndexLongFieldRange; import org.elasticsearch.indices.SystemIndexDescriptor; import org.elasticsearch.xcontent.ToXContent; @@ -1039,12 +1038,6 @@ public static ClusterState readFrom(StreamInput in, DiscoveryNode localNode) thr return builder.build(); } - /** - * If the cluster state does not contain transport version information, this is the version - * that is inferred for all nodes on version 8.8.0 or above. - */ - @UpdateForV9(owner = UpdateForV9.Owner.CORE_INFRA) - public static final TransportVersion INFERRED_TRANSPORT_VERSION = TransportVersions.V_8_8_0; public static final Version VERSION_INTRODUCING_TRANSPORT_VERSIONS = Version.V_8_8_0; @Override diff --git a/server/src/main/java/org/elasticsearch/env/BuildVersion.java b/server/src/main/java/org/elasticsearch/env/BuildVersion.java index 5c3602283fef3..877a07d9a3ee5 100644 --- a/server/src/main/java/org/elasticsearch/env/BuildVersion.java +++ b/server/src/main/java/org/elasticsearch/env/BuildVersion.java @@ -73,6 +73,12 @@ public abstract class BuildVersion implements ToXContentFragment, Writeable { */ public abstract String toNodeMetadata(); + /** + * Returns the minimum compatible build version based on the current version. + * Ie a node needs to have at least the return version in order to communicate with a node running the current version. + */ + public abstract BuildVersion minimumCompatibilityVersion(); + /** * Create a {@link BuildVersion} from a version ID number. * diff --git a/server/src/main/java/org/elasticsearch/env/DefaultBuildVersion.java b/server/src/main/java/org/elasticsearch/env/DefaultBuildVersion.java index 913a352debfb8..39b9278a78c97 100644 --- a/server/src/main/java/org/elasticsearch/env/DefaultBuildVersion.java +++ b/server/src/main/java/org/elasticsearch/env/DefaultBuildVersion.java @@ -73,6 +73,11 @@ public String toNodeMetadata() { return Integer.toString(version.id()); } + @Override + public BuildVersion minimumCompatibilityVersion() { + return fromVersionId(version.minimumCompatibilityVersion().id); + } + @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { return builder.value(version.id()); diff --git a/server/src/main/java/org/elasticsearch/env/NodeEnvironment.java b/server/src/main/java/org/elasticsearch/env/NodeEnvironment.java index afadb8f5b3011..90e2ae5c62703 100644 --- a/server/src/main/java/org/elasticsearch/env/NodeEnvironment.java +++ b/server/src/main/java/org/elasticsearch/env/NodeEnvironment.java @@ -86,8 +86,6 @@ import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -525,7 +523,7 @@ static void checkForIndexCompatibility(Logger logger, DataPath... dataPaths) thr logger.info("oldest index version recorded in NodeMetadata {}", metadata.oldestIndexVersion()); if (metadata.oldestIndexVersion().before(IndexVersions.MINIMUM_COMPATIBLE)) { - String bestDowngradeVersion = getBestDowngradeVersion(metadata.previousNodeVersion().toString()); + BuildVersion bestDowngradeVersion = getBestDowngradeVersion(metadata.previousNodeVersion()); throw new IllegalStateException( "Cannot start this node because it holds metadata for indices with version [" + metadata.oldestIndexVersion().toReleaseVersion() @@ -1504,28 +1502,17 @@ private static void tryWriteTempFile(Path path) throws IOException { /** * Get a useful version string to direct a user's downgrade operation * - *

If a user is trying to install 8.0 but has incompatible indices, the user should - * downgrade to 7.17.x. We return 7.17.0, unless the user is trying to upgrade from - * a 7.17.x release, in which case we return the last installed version. + *

If a user is trying to install current major N but has incompatible indices, the user should + * downgrade to last minor of the previous major (N-1).last. We return (N-1).last, unless the user is trying to upgrade from + * a (N-1).last.x release, in which case we return the last installed version. * @return Version to downgrade to */ // visible for testing - static String getBestDowngradeVersion(String previousNodeVersion) { - // this method should only be called in the context of an upgrade to 8.x - assert Build.current().version().startsWith("9.") == false; - Pattern pattern = Pattern.compile("^7\\.(\\d+)\\.\\d+$"); - Matcher matcher = pattern.matcher(previousNodeVersion); - if (matcher.matches()) { - try { - int minorVersion = Integer.parseInt(matcher.group(1)); - if (minorVersion >= 17) { - return previousNodeVersion; - } - } catch (NumberFormatException e) { - // continue and return default - } + static BuildVersion getBestDowngradeVersion(BuildVersion previousNodeVersion) { + if (previousNodeVersion.onOrAfterMinimumCompatible()) { + return previousNodeVersion; } - return "7.17.0"; + return BuildVersion.current().minimumCompatibilityVersion(); } } diff --git a/server/src/main/java/org/elasticsearch/env/NodeMetadata.java b/server/src/main/java/org/elasticsearch/env/NodeMetadata.java index c71a3798be1f7..48268b5001f3e 100644 --- a/server/src/main/java/org/elasticsearch/env/NodeMetadata.java +++ b/server/src/main/java/org/elasticsearch/env/NodeMetadata.java @@ -10,7 +10,6 @@ package org.elasticsearch.env; import org.elasticsearch.Build; -import org.elasticsearch.core.UpdateForV9; import org.elasticsearch.gateway.MetadataStateFormat; import org.elasticsearch.index.IndexVersion; import org.elasticsearch.index.IndexVersions; @@ -158,12 +157,11 @@ public void setOldestIndexVersion(int oldestIndexVersion) { this.oldestIndexVersion = IndexVersion.fromId(oldestIndexVersion); } - @UpdateForV9(owner = UpdateForV9.Owner.CORE_INFRA) // version is required in the node metadata from v9 onwards public NodeMetadata build() { final IndexVersion oldestIndexVersion; if (this.nodeVersion == null) { - nodeVersion = BuildVersion.fromVersionId(0); + throw new IllegalStateException("Node version is required in node metadata"); } if (this.previousNodeVersion == null) { previousNodeVersion = nodeVersion; diff --git a/server/src/main/java/org/elasticsearch/index/engine/Engine.java b/server/src/main/java/org/elasticsearch/index/engine/Engine.java index 394de0684c104..36fd18144ad6e 100644 --- a/server/src/main/java/org/elasticsearch/index/engine/Engine.java +++ b/server/src/main/java/org/elasticsearch/index/engine/Engine.java @@ -75,6 +75,7 @@ import org.elasticsearch.index.seqno.SequenceNumbers; import org.elasticsearch.index.shard.DenseVectorStats; import org.elasticsearch.index.shard.DocsStats; +import org.elasticsearch.index.shard.IndexShard; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.index.shard.ShardLongFieldRange; import org.elasticsearch.index.shard.SparseVectorStats; @@ -2334,4 +2335,15 @@ public record FlushResult(boolean flushPerformed, long generation) { public static final long UNKNOWN_GENERATION = -1L; public static final FlushResult NO_FLUSH = new FlushResult(false, UNKNOWN_GENERATION); } + + /** + * Ensures the engine is in a state that it can be closed by a call to {@link IndexShard#resetEngine()}. + * + * In general, resetting the engine should be done with care, to consider any + * in-progress operations and listeners (e.g., primary term and generation listeners). + * At the moment, this is implemented in serverless for a special case that ensures the engine is prepared for reset. + */ + public void prepareForEngineReset() throws IOException { + throw new UnsupportedOperationException("does not support engine reset"); + } } diff --git a/server/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java b/server/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java index 9ddb6f0d496a0..ac3f019636b66 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java @@ -57,6 +57,7 @@ public final class DocumentParser { static final NodeFeature FIX_PARSING_SUBOBJECTS_FALSE_DYNAMIC_FALSE = new NodeFeature( "mapper.fix_parsing_subobjects_false_dynamic_false" ); + private static final String NOOP_FIELD_MAPPER_NAME = "no-op"; private final XContentParserConfiguration parserConfiguration; private final MappingParserContext mappingParserContext; @@ -706,6 +707,8 @@ private static void parseNonDynamicArray( canRemoveSingleLeafElement = mapper instanceof FieldMapper && mode == Mapper.SourceKeepMode.ARRAYS + && context.inArrayScope() == false + && mapper.leafName().equals(NOOP_FIELD_MAPPER_NAME) == false && fieldWithFallbackSyntheticSource == false && copyToFieldHasValuesInDocument == false; @@ -732,10 +735,10 @@ private static void parseNonDynamicArray( int elements = 0; while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { if (token == XContentParser.Token.START_OBJECT) { - elements = Integer.MAX_VALUE; + elements = 2; parseObject(context, lastFieldName); } else if (token == XContentParser.Token.START_ARRAY) { - elements = Integer.MAX_VALUE; + elements = 2; parseArray(context, lastFieldName); } else if (token == XContentParser.Token.VALUE_NULL) { elements++; @@ -925,22 +928,26 @@ private static Mapper getLeafMapper(final DocumentParserContext context, String } private static FieldMapper noopFieldMapper(String path) { - return new FieldMapper("no-op", new MappedFieldType("no-op", false, false, false, TextSearchInfo.NONE, Collections.emptyMap()) { - @Override - public ValueFetcher valueFetcher(SearchExecutionContext context, String format) { - throw new UnsupportedOperationException(); - } + return new FieldMapper( + NOOP_FIELD_MAPPER_NAME, + new MappedFieldType(NOOP_FIELD_MAPPER_NAME, false, false, false, TextSearchInfo.NONE, Collections.emptyMap()) { + @Override + public ValueFetcher valueFetcher(SearchExecutionContext context, String format) { + throw new UnsupportedOperationException(); + } - @Override - public String typeName() { - throw new UnsupportedOperationException(); - } + @Override + public String typeName() { + throw new UnsupportedOperationException(); + } - @Override - public Query termQuery(Object value, SearchExecutionContext context) { - throw new UnsupportedOperationException(); - } - }, FieldMapper.BuilderParams.empty()) { + @Override + public Query termQuery(Object value, SearchExecutionContext context) { + throw new UnsupportedOperationException(); + } + }, + FieldMapper.BuilderParams.empty() + ) { @Override protected void parseCreateField(DocumentParserContext context) { diff --git a/server/src/main/java/org/elasticsearch/index/shard/IndexShard.java b/server/src/main/java/org/elasticsearch/index/shard/IndexShard.java index ab1c936d1c469..bfa286858f8ba 100644 --- a/server/src/main/java/org/elasticsearch/index/shard/IndexShard.java +++ b/server/src/main/java/org/elasticsearch/index/shard/IndexShard.java @@ -4307,6 +4307,35 @@ public void afterRefresh(boolean didRefresh) { } } + /** + * Reset the current engine to a new one. + * + * Calls {@link Engine#prepareForEngineReset()} on the current engine, then closes it, and loads a new engine without + * doing any translog recovery. + * + * In general, resetting the engine should be done with care, to consider any in-progress operations and listeners. + * At the moment, this is implemented in serverless for a special case that ensures the engine is prepared for reset. + */ + public void resetEngine() { + assert Thread.holdsLock(mutex) == false : "resetting engine under mutex"; + assert waitForEngineOrClosedShardListeners.isDone(); + try { + synchronized (engineMutex) { + final var currentEngine = getEngine(); + currentEngine.prepareForEngineReset(); + var engineConfig = newEngineConfig(replicationTracker); + verifyNotClosed(); + IOUtils.close(currentEngine); + var newEngine = createEngine(engineConfig); + currentEngineReference.set(newEngine); + onNewEngine(newEngine); + } + onSettingsChanged(); + } catch (Exception e) { + failShard("unable to reset engine", e); + } + } + /** * Rollback the current engine to the safe commit, then replay local translog up to the global checkpoint. */ diff --git a/server/src/main/java/org/elasticsearch/ingest/IngestCtxMap.java b/server/src/main/java/org/elasticsearch/ingest/IngestCtxMap.java index d903cc8e52144..a5a1612246a29 100644 --- a/server/src/main/java/org/elasticsearch/ingest/IngestCtxMap.java +++ b/server/src/main/java/org/elasticsearch/ingest/IngestCtxMap.java @@ -29,7 +29,7 @@ * * The map is expected to be used by processors, server code should the typed getter and setters where possible. */ -class IngestCtxMap extends CtxMap { +final class IngestCtxMap extends CtxMap { /** * Create an IngestCtxMap with the given metadata, source and default validators diff --git a/server/src/main/java/org/elasticsearch/ingest/IngestDocMetadata.java b/server/src/main/java/org/elasticsearch/ingest/IngestDocMetadata.java index 5afeb5079a43f..d7533e26f3df0 100644 --- a/server/src/main/java/org/elasticsearch/ingest/IngestDocMetadata.java +++ b/server/src/main/java/org/elasticsearch/ingest/IngestDocMetadata.java @@ -19,7 +19,7 @@ import java.util.Map; import java.util.stream.Collectors; -class IngestDocMetadata extends Metadata { +final class IngestDocMetadata extends Metadata { static final Map> PROPERTIES = Map.of( INDEX, @@ -42,6 +42,17 @@ class IngestDocMetadata extends Metadata { new FieldProperty<>(Map.class).withWritable().withNullable() ); + private static final char UNDERSCORE = '_'; + static { + // there's an optimization here in the overridden isAvailable below, but it only works if the first character of each of these + // keys starts with an underscore, since we know all the keys up front, though, we can just make sure that's always true + for (String key : PROPERTIES.keySet()) { + if (key.charAt(0) != UNDERSCORE) { + throw new IllegalArgumentException("IngestDocMetadata keys must begin with an underscore, but found [" + key + "]"); + } + } + } + protected final ZonedDateTime timestamp; IngestDocMetadata(String index, String id, long version, String routing, VersionType versionType, ZonedDateTime timestamp) { @@ -49,11 +60,7 @@ class IngestDocMetadata extends Metadata { } IngestDocMetadata(Map metadata, ZonedDateTime timestamp) { - this(metadata, PROPERTIES, timestamp); - } - - IngestDocMetadata(Map metadata, Map> properties, ZonedDateTime timestamp) { - super(metadata, properties); + super(metadata, PROPERTIES); this.timestamp = timestamp; } @@ -100,4 +107,16 @@ private static void versionTypeValidator(String key, String value) { + "]" ); } + + @Override + public boolean isAvailable(String key) { + // the key cannot be null or empty because of the nature of the calling code, and this is already validated in IngestDocument + assert key != null && key.isEmpty() == false; + // we can avoid a map lookup on most keys since we know that the only keys that are 'metadata keys' for an ingest document + // must be keys that start with an underscore + if (key.charAt(0) != UNDERSCORE) { + return false; + } + return super.isAvailable(key); + } } diff --git a/server/src/main/java/org/elasticsearch/ingest/IngestDocument.java b/server/src/main/java/org/elasticsearch/ingest/IngestDocument.java index 0614e9e92edf2..7982024911beb 100644 --- a/server/src/main/java/org/elasticsearch/ingest/IngestDocument.java +++ b/server/src/main/java/org/elasticsearch/ingest/IngestDocument.java @@ -56,6 +56,9 @@ public final class IngestDocument { // This is the maximum number of nested pipelines that can be within a pipeline. If there are more, we bail out with an error public static final int MAX_PIPELINES = Integer.parseInt(System.getProperty("es.ingest.max_pipelines", "100")); + // a 'not found' sentinel value for use in getOrDefault calls in order to avoid containsKey-and-then-get + private static final Object NOT_FOUND = new Object(); + private final IngestCtxMap ctxMap; private final Map ingestMetadata; @@ -376,11 +379,15 @@ private static ResolveResult resolve(String pathElement, String fullPath, Object if (context == null) { return ResolveResult.error("cannot resolve [" + pathElement + "] from null as part of path [" + fullPath + "]"); } - if (context instanceof Map map) { - if (map.containsKey(pathElement)) { - return ResolveResult.success(map.get(pathElement)); + if (context instanceof Map) { + @SuppressWarnings("unchecked") + Map map = (Map) context; + Object object = map.getOrDefault(pathElement, NOT_FOUND); // getOrDefault is faster than containsKey + get + if (object == NOT_FOUND) { + return ResolveResult.error("field [" + pathElement + "] not present as part of path [" + fullPath + "]"); + } else { + return ResolveResult.success(object); } - return ResolveResult.error("field [" + pathElement + "] not present as part of path [" + fullPath + "]"); } if (context instanceof List list) { int index; @@ -547,12 +554,13 @@ private void setFieldValue(String path, Object value, boolean append, boolean al if (context instanceof Map) { @SuppressWarnings("unchecked") Map map = (Map) context; - if (map.containsKey(pathElement)) { - context = map.get(pathElement); - } else { - HashMap newMap = new HashMap<>(); + Object object = map.getOrDefault(pathElement, NOT_FOUND); // getOrDefault is faster than containsKey + get + if (object == NOT_FOUND) { + Map newMap = new HashMap<>(); map.put(pathElement, newMap); context = newMap; + } else { + context = object; } } else if (context instanceof List list) { int index; @@ -591,16 +599,16 @@ private void setFieldValue(String path, Object value, boolean append, boolean al @SuppressWarnings("unchecked") Map map = (Map) context; if (append) { - if (map.containsKey(leafKey)) { - Object object = map.get(leafKey); + Object object = map.getOrDefault(leafKey, NOT_FOUND); // getOrDefault is faster than containsKey + get + if (object == NOT_FOUND) { + List list = new ArrayList<>(); + appendValues(list, value); + map.put(leafKey, list); + } else { Object list = appendValues(object, value, allowDuplicates); if (list != object) { map.put(leafKey, list); } - } else { - List list = new ArrayList<>(); - appendValues(list, value); - map.put(leafKey, list); } return; } diff --git a/server/src/main/java/org/elasticsearch/ingest/IngestService.java b/server/src/main/java/org/elasticsearch/ingest/IngestService.java index 86522742a66c0..b819a1686d23c 100644 --- a/server/src/main/java/org/elasticsearch/ingest/IngestService.java +++ b/server/src/main/java/org/elasticsearch/ingest/IngestService.java @@ -1204,8 +1204,9 @@ private static void updateIndexRequestMetadata(final IndexRequest request, final request.id(metadata.getId()); request.routing(metadata.getRouting()); request.version(metadata.getVersion()); - if (metadata.getVersionType() != null) { - request.versionType(VersionType.fromString(metadata.getVersionType())); + String versionType; + if ((versionType = metadata.getVersionType()) != null) { + request.versionType(VersionType.fromString(versionType)); } Number number; if ((number = metadata.getIfSeqNo()) != null) { diff --git a/server/src/main/java/org/elasticsearch/rest/action/cat/RestCatComponentTemplateAction.java b/server/src/main/java/org/elasticsearch/rest/action/cat/RestCatComponentTemplateAction.java index 0efb42b56a351..6ec55b1237324 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/cat/RestCatComponentTemplateAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/cat/RestCatComponentTemplateAction.java @@ -54,7 +54,7 @@ public List routes() { @Override protected void documentation(StringBuilder sb) { - sb.append("/_cat/component_templates"); + sb.append("/_cat/component_templates\n"); } @Override diff --git a/server/src/main/java/org/elasticsearch/script/CtxMap.java b/server/src/main/java/org/elasticsearch/script/CtxMap.java index e790eed097f35..342e37efcaedf 100644 --- a/server/src/main/java/org/elasticsearch/script/CtxMap.java +++ b/server/src/main/java/org/elasticsearch/script/CtxMap.java @@ -14,6 +14,7 @@ import java.util.AbstractCollection; import java.util.AbstractMap; import java.util.AbstractSet; +import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; @@ -150,10 +151,12 @@ public Object remove(Object key) { @Override public void clear() { // AbstractMap uses entrySet().clear(), it should be quicker to run through the validators, then call the wrapped maps clear - for (String key : metadata.keySet()) { + for (String key : new ArrayList<>(metadata.keySet())) { // copy the key set to get around the ConcurrentModificationException metadata.remove(key); } - // TODO: this is just bogus, there isn't any case where metadata won't trip a failure above? + // note: this is actually bogus in the general case, though! for this to work there must be some Metadata or subclass of Metadata + // for which all the FieldPoperty properties of the metadata are nullable and therefore could have been removed in the previous + // loop -- does such a class even exist? (that is, is there any *real* CtxMap for which the previous loop didn't throw?) source.clear(); } @@ -193,6 +196,18 @@ public Object get(Object key) { return directSourceAccess() ? source.get(key) : (SOURCE.equals(key) ? source : null); } + @Override + public Object getOrDefault(Object key, Object defaultValue) { + // uses map directly to avoid Map's implementation that is just get and then containsKey and so could require two isAvailable calls + if (key instanceof String str) { + if (metadata.isAvailable(str)) { + return metadata.getOrDefault(str, defaultValue); + } + return directSourceAccess() ? source.getOrDefault(key, defaultValue) : (SOURCE.equals(key) ? source : defaultValue); + } + return defaultValue; + } + /** * Set of entries of the wrapped map that calls the appropriate validator before changing an entries value or removing an entry. * diff --git a/server/src/main/java/org/elasticsearch/script/Metadata.java b/server/src/main/java/org/elasticsearch/script/Metadata.java index dc5ae51e45af0..fc2a59f7171f2 100644 --- a/server/src/main/java/org/elasticsearch/script/Metadata.java +++ b/server/src/main/java/org/elasticsearch/script/Metadata.java @@ -240,6 +240,13 @@ public Object get(String key) { return map.get(key); } + /** + * Get the value associated with {@param key}, otherwise return {@param defaultValue} + */ + public Object getOrDefault(String key, Object defaultValue) { + return map.getOrDefault(key, defaultValue); + } + /** * Remove the mapping associated with {@param key} * @throws IllegalArgumentException if {@link #isAvailable(String)} is false or the key cannot be removed. diff --git a/server/src/main/java/org/elasticsearch/search/SearchService.java b/server/src/main/java/org/elasticsearch/search/SearchService.java index 7f3747d321972..efa27b2f3448c 100644 --- a/server/src/main/java/org/elasticsearch/search/SearchService.java +++ b/server/src/main/java/org/elasticsearch/search/SearchService.java @@ -957,32 +957,38 @@ public void executeFetchPhase(ShardFetchRequest request, SearchShardTask task, A final ReaderContext readerContext = findReaderContext(request.contextId(), request); final ShardSearchRequest shardSearchRequest = readerContext.getShardSearchRequest(request.getShardSearchRequest()); final Releasable markAsUsed = readerContext.markAsUsed(getKeepAlive(shardSearchRequest)); - runAsync(getExecutor(readerContext.indexShard()), () -> { - try (SearchContext searchContext = createContext(readerContext, shardSearchRequest, task, ResultsType.FETCH, false)) { - if (request.lastEmittedDoc() != null) { - searchContext.scrollContext().lastEmittedDoc = request.lastEmittedDoc(); - } - searchContext.assignRescoreDocIds(readerContext.getRescoreDocIds(request.getRescoreDocIds())); - searchContext.searcher().setAggregatedDfs(readerContext.getAggregatedDfs(request.getAggregatedDfs())); - try ( - SearchOperationListenerExecutor executor = new SearchOperationListenerExecutor(searchContext, true, System.nanoTime()) - ) { - fetchPhase.execute(searchContext, request.docIds(), request.getRankDocks()); - if (readerContext.singleSession()) { - freeReaderContext(request.contextId()); + rewriteAndFetchShardRequest(readerContext.indexShard(), shardSearchRequest, listener.delegateFailure((l, rewritten) -> { + runAsync(getExecutor(readerContext.indexShard()), () -> { + try (SearchContext searchContext = createContext(readerContext, rewritten, task, ResultsType.FETCH, false)) { + if (request.lastEmittedDoc() != null) { + searchContext.scrollContext().lastEmittedDoc = request.lastEmittedDoc(); } - executor.success(); + searchContext.assignRescoreDocIds(readerContext.getRescoreDocIds(request.getRescoreDocIds())); + searchContext.searcher().setAggregatedDfs(readerContext.getAggregatedDfs(request.getAggregatedDfs())); + try ( + SearchOperationListenerExecutor executor = new SearchOperationListenerExecutor( + searchContext, + true, + System.nanoTime() + ) + ) { + fetchPhase.execute(searchContext, request.docIds(), request.getRankDocks()); + if (readerContext.singleSession()) { + freeReaderContext(request.contextId()); + } + executor.success(); + } + var fetchResult = searchContext.fetchResult(); + // inc-ref fetch result because we close the SearchContext that references it in this try-with-resources block + fetchResult.incRef(); + return fetchResult; + } catch (Exception e) { + assert TransportActions.isShardNotAvailableException(e) == false : new AssertionError(e); + // we handle the failure in the failure listener below + throw e; } - var fetchResult = searchContext.fetchResult(); - // inc-ref fetch result because we close the SearchContext that references it in this try-with-resources block - fetchResult.incRef(); - return fetchResult; - } catch (Exception e) { - assert TransportActions.isShardNotAvailableException(e) == false : new AssertionError(e); - // we handle the failure in the failure listener below - throw e; - } - }, wrapFailureListener(listener, readerContext, markAsUsed)); + }, wrapFailureListener(l, readerContext, markAsUsed)); + })); } protected void checkCancelled(SearchShardTask task) { diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/AggregationPhase.java b/server/src/main/java/org/elasticsearch/search/aggregations/AggregationPhase.java index 8ca21db1ad9f2..4e1ec3faf6b36 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/AggregationPhase.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/AggregationPhase.java @@ -8,13 +8,10 @@ */ package org.elasticsearch.search.aggregations; -import org.elasticsearch.action.search.SearchShardTask; import org.elasticsearch.search.aggregations.support.TimeSeriesIndexSearcher; import org.elasticsearch.search.internal.SearchContext; -import org.elasticsearch.search.query.QueryPhase; import java.io.IOException; -import java.util.ArrayList; import java.util.List; import java.util.function.Supplier; @@ -59,7 +56,7 @@ private static AggregatorCollector newAggregatorCollector(SearchContext context) } private static void executeInSortOrder(SearchContext context, BucketCollector collector) { - TimeSeriesIndexSearcher searcher = new TimeSeriesIndexSearcher(context.searcher(), getCancellationChecks(context)); + TimeSeriesIndexSearcher searcher = new TimeSeriesIndexSearcher(context.searcher(), context.getCancellationChecks()); searcher.setMinimumScore(context.minimumScore()); searcher.setProfiler(context); try { @@ -70,23 +67,4 @@ private static void executeInSortOrder(SearchContext context, BucketCollector co } } - private static List getCancellationChecks(SearchContext context) { - List cancellationChecks = new ArrayList<>(); - if (context.lowLevelCancellation()) { - // This searching doesn't live beyond this phase, so we don't need to remove query cancellation - cancellationChecks.add(() -> { - final SearchShardTask task = context.getTask(); - if (task != null) { - task.ensureNotCancelled(); - } - }); - } - - final Runnable timeoutRunnable = QueryPhase.getTimeoutCheck(context); - if (timeoutRunnable != null) { - cancellationChecks.add(timeoutRunnable); - } - - return cancellationChecks; - } } diff --git a/server/src/main/java/org/elasticsearch/search/internal/SearchContext.java b/server/src/main/java/org/elasticsearch/search/internal/SearchContext.java index 14e2007befa55..7da71b77c6a6f 100644 --- a/server/src/main/java/org/elasticsearch/search/internal/SearchContext.java +++ b/server/src/main/java/org/elasticsearch/search/internal/SearchContext.java @@ -41,6 +41,7 @@ import org.elasticsearch.search.fetch.subphase.ScriptFieldsContext; import org.elasticsearch.search.fetch.subphase.highlight.SearchHighlightContext; import org.elasticsearch.search.profile.Profilers; +import org.elasticsearch.search.query.QueryPhase; import org.elasticsearch.search.query.QuerySearchResult; import org.elasticsearch.search.rank.context.QueryPhaseRankShardContext; import org.elasticsearch.search.rank.feature.RankFeatureResult; @@ -84,6 +85,21 @@ public abstract class SearchContext implements Releasable { protected SearchContext() {} + public final List getCancellationChecks() { + final Runnable timeoutRunnable = QueryPhase.getTimeoutCheck(this); + if (lowLevelCancellation()) { + // This searching doesn't live beyond this phase, so we don't need to remove query cancellation + Runnable c = () -> { + final SearchShardTask task = getTask(); + if (task != null) { + task.ensureNotCancelled(); + } + }; + return timeoutRunnable == null ? List.of(c) : List.of(c, timeoutRunnable); + } + return timeoutRunnable == null ? List.of() : List.of(timeoutRunnable); + } + public abstract void setTask(SearchShardTask task); public abstract SearchShardTask getTask(); diff --git a/server/src/main/java/org/elasticsearch/search/rescore/RescorePhase.java b/server/src/main/java/org/elasticsearch/search/rescore/RescorePhase.java index c23df9cdfa441..f8b348b383f01 100644 --- a/server/src/main/java/org/elasticsearch/search/rescore/RescorePhase.java +++ b/server/src/main/java/org/elasticsearch/search/rescore/RescorePhase.java @@ -15,19 +15,16 @@ import org.apache.lucene.search.TopDocs; import org.apache.lucene.search.TopFieldDocs; import org.elasticsearch.ElasticsearchException; -import org.elasticsearch.action.search.SearchShardTask; import org.elasticsearch.common.lucene.search.TopDocsAndMaxScore; import org.elasticsearch.common.util.Maps; import org.elasticsearch.lucene.grouping.TopFieldGroups; import org.elasticsearch.search.internal.ContextIndexSearcher; import org.elasticsearch.search.internal.SearchContext; -import org.elasticsearch.search.query.QueryPhase; import org.elasticsearch.search.query.SearchTimeoutException; import org.elasticsearch.search.sort.ShardDocSortField; import org.elasticsearch.search.sort.SortAndFormats; import java.io.IOException; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -195,21 +192,7 @@ private static boolean topDocsSortedByScore(TopDocs topDocs) { } static Runnable getCancellationChecks(SearchContext context) { - List cancellationChecks = new ArrayList<>(); - if (context.lowLevelCancellation()) { - cancellationChecks.add(() -> { - final SearchShardTask task = context.getTask(); - if (task != null) { - task.ensureNotCancelled(); - } - }); - } - - final Runnable timeoutRunnable = QueryPhase.getTimeoutCheck(context); - if (timeoutRunnable != null) { - cancellationChecks.add(timeoutRunnable); - } - + List cancellationChecks = context.getCancellationChecks(); return () -> { for (var check : cancellationChecks) { check.run(); diff --git a/server/src/main/java/org/elasticsearch/transport/TransportHandshaker.java b/server/src/main/java/org/elasticsearch/transport/TransportHandshaker.java index a5973e4001444..1a9043d093feb 100644 --- a/server/src/main/java/org/elasticsearch/transport/TransportHandshaker.java +++ b/server/src/main/java/org/elasticsearch/transport/TransportHandshaker.java @@ -53,7 +53,7 @@ final class TransportHandshaker { * rely on them matching the real transport protocol (which itself matched the release version numbers), but these days that's no longer * true. * - * Here are some example messages, broken down to show their structure: + * Here are some example messages, broken down to show their structure. See TransportHandshakerRawMessageTests for supporting tests. * * ## v6080099 Request: * @@ -87,7 +87,7 @@ final class TransportHandshaker { * c3 f9 eb 03 -- max acceptable protocol version (vInt: 00000011 11101011 11111001 11000011 == 8060099) * * - * ## v7170099 and v8800000 Requests: + * ## v7170099 Requests: * * 45 53 -- 'ES' marker * 00 00 00 31 -- total message length @@ -106,7 +106,7 @@ final class TransportHandshaker { * 04 -- payload length * c3 f9 eb 03 -- max acceptable protocol version (vInt: 00000011 11101011 11111001 11000011 == 8060099) * - * ## v7170099 and v8800000 Responses: + * ## v7170099 Responses: * * 45 53 -- 'ES' marker * 00 00 00 17 -- total message length @@ -118,6 +118,40 @@ final class TransportHandshaker { * 00 -- no response headers [1] * c3 f9 eb 03 -- max acceptable protocol version (vInt: 00000011 11101011 11111001 11000011 == 8060099) * + * ## v8800000 Requests: + * + * 45 53 -- 'ES' marker + * 00 00 00 36 -- total message length + * 00 00 00 00 00 00 00 01 -- request ID + * 08 -- status flags (0b1000 == handshake request) + * 00 86 47 00 -- handshake protocol version (0x6d6833 == 7170099) + * 00 00 00 19 -- length of variable portion of header + * 00 -- no request headers [1] + * 00 -- no response headers [1] + * 16 -- action string size + * 69 6e 74 65 72 6e 61 6c } + * 3a 74 63 70 2f 68 61 6e }- ASCII representation of HANDSHAKE_ACTION_NAME + * 64 73 68 61 6b 65 } + * 00 -- no parent task ID [3] + * 0a -- payload length + * e8 8f 9b 04 -- requesting node transport version (vInt: 00000100 10011011 10001111 11101000 == 8833000) + * 05 -- requesting node release version string length + * 39 2e 30 2e 30 -- requesting node release version string "9.0.0" + * + * ## v8800000 Responses: + * + * 45 53 -- 'ES' marker + * 00 00 00 1d -- total message length + * 00 00 00 00 00 00 00 01 -- request ID (copied from request) + * 09 -- status flags (0b1001 == handshake response) + * 00 86 47 00 -- handshake protocol version (0x864700 == 8800000, copied from request) + * 00 00 00 02 -- length of following variable portion of header + * 00 -- no request headers [1] + * 00 -- no response headers [1] + * e8 8f 9b 04 -- responding node transport version (vInt: 00000100 10011011 10001111 11101000 == 8833000) + * 05 -- responding node release version string length + * 39 2e 30 2e 30 -- responding node release version string "9.0.0" + * * [1] Thread context headers should be empty; see org.elasticsearch.common.util.concurrent.ThreadContext.ThreadContextStruct.writeTo * for their structure. * [2] A list of strings, which can safely be ignored diff --git a/server/src/main/resources/org/elasticsearch/bootstrap/test-framework.policy b/server/src/main/resources/org/elasticsearch/bootstrap/test-framework.policy index 462fab651c211..77aae99907dfc 100644 --- a/server/src/main/resources/org/elasticsearch/bootstrap/test-framework.policy +++ b/server/src/main/resources/org/elasticsearch/bootstrap/test-framework.policy @@ -127,6 +127,7 @@ grant codeBase "${codebase.netty-transport}" { grant { permission java.net.SocketPermission "127.0.0.1", "accept, connect, resolve"; + permission java.net.SocketPermission "[0:0:0:0:0:0:0:1]", "accept, connect, resolve"; permission java.nio.file.LinkPermission "symbolic"; // needed for keystore tests permission java.lang.RuntimePermission "accessUserInformation"; diff --git a/server/src/test/java/org/elasticsearch/env/BuildVersionTests.java b/server/src/test/java/org/elasticsearch/env/BuildVersionTests.java index 9fd889426fd2d..96875ac1a95e5 100644 --- a/server/src/test/java/org/elasticsearch/env/BuildVersionTests.java +++ b/server/src/test/java/org/elasticsearch/env/BuildVersionTests.java @@ -43,6 +43,16 @@ public void testIsFutureVersion() { assertTrue(futureVersion.isFutureVersion()); } + public void testMinimumCompatibilityVersion() { + BuildVersion minCompatible = BuildVersion.fromVersionId(Version.CURRENT.minimumCompatibilityVersion().id()); + assertThat(BuildVersion.current().minimumCompatibilityVersion(), equalTo(minCompatible)); + + BuildVersion previousCompatible = BuildVersion.fromVersionId( + Version.CURRENT.minimumCompatibilityVersion().minimumCompatibilityVersion().id() + ); + assertThat(minCompatible.minimumCompatibilityVersion(), equalTo(previousCompatible)); + } + public static BuildVersion increment(BuildVersion version) { return BuildVersion.fromVersionId(((DefaultBuildVersion) version).version.id() + 1); } diff --git a/server/src/test/java/org/elasticsearch/env/NodeEnvironmentTests.java b/server/src/test/java/org/elasticsearch/env/NodeEnvironmentTests.java index 42a94ebf8c6ff..82934bda2d259 100644 --- a/server/src/test/java/org/elasticsearch/env/NodeEnvironmentTests.java +++ b/server/src/test/java/org/elasticsearch/env/NodeEnvironmentTests.java @@ -584,7 +584,9 @@ public void testIndexCompatibilityChecks() throws IOException { containsString("it holds metadata for indices with version [" + oldIndexVersion.toReleaseVersion() + "]"), containsString( "Revert this node to version [" - + (previousNodeVersion.major == Version.V_8_0_0.major ? Version.V_7_17_0 : previousNodeVersion) + + (previousNodeVersion.major == Version.CURRENT.major + ? Version.CURRENT.minimumCompatibilityVersion() + : previousNodeVersion) + "]" ) ) @@ -638,20 +640,31 @@ public void testSymlinkDataDirectory() throws Exception { env.close(); } - @UpdateForV9(owner = UpdateForV9.Owner.CORE_INFRA) - @AwaitsFix(bugUrl = "test won't work until we remove and bump minimum index versions") public void testGetBestDowngradeVersion() { - assertThat(NodeEnvironment.getBestDowngradeVersion("7.17.0"), Matchers.equalTo("7.17.0")); - assertThat(NodeEnvironment.getBestDowngradeVersion("7.17.5"), Matchers.equalTo("7.17.5")); - assertThat(NodeEnvironment.getBestDowngradeVersion("7.17.1234"), Matchers.equalTo("7.17.1234")); - assertThat(NodeEnvironment.getBestDowngradeVersion("7.18.0"), Matchers.equalTo("7.18.0")); - assertThat(NodeEnvironment.getBestDowngradeVersion("7.17.x"), Matchers.equalTo("7.17.0")); - assertThat(NodeEnvironment.getBestDowngradeVersion("7.17.5-SNAPSHOT"), Matchers.equalTo("7.17.0")); - assertThat(NodeEnvironment.getBestDowngradeVersion("7.17.6b"), Matchers.equalTo("7.17.0")); - assertThat(NodeEnvironment.getBestDowngradeVersion("7.16.0"), Matchers.equalTo("7.17.0")); - // when we get to version 7.2147483648.0 we will have to rethink our approach, but for now we return 7.17.0 with an integer overflow - assertThat(NodeEnvironment.getBestDowngradeVersion("7." + Integer.MAX_VALUE + "0.0"), Matchers.equalTo("7.17.0")); - assertThat(NodeEnvironment.getBestDowngradeVersion("foo"), Matchers.equalTo("7.17.0")); + assertThat( + NodeEnvironment.getBestDowngradeVersion(BuildVersion.fromString("8.18.0")), + Matchers.equalTo(BuildVersion.fromString("8.18.0")) + ); + assertThat( + NodeEnvironment.getBestDowngradeVersion(BuildVersion.fromString("8.18.5")), + Matchers.equalTo(BuildVersion.fromString("8.18.5")) + ); + assertThat( + NodeEnvironment.getBestDowngradeVersion(BuildVersion.fromString("8.18.12")), + Matchers.equalTo(BuildVersion.fromString("8.18.12")) + ); + assertThat( + NodeEnvironment.getBestDowngradeVersion(BuildVersion.fromString("8.19.0")), + Matchers.equalTo(BuildVersion.fromString("8.19.0")) + ); + assertThat( + NodeEnvironment.getBestDowngradeVersion(BuildVersion.fromString("8.17.0")), + Matchers.equalTo(BuildVersion.fromString("8.18.0")) + ); + assertThat( + NodeEnvironment.getBestDowngradeVersion(BuildVersion.fromString("7.17.0")), + Matchers.equalTo(BuildVersion.fromString("8.18.0")) + ); } private void verifyFailsOnShardData(Settings settings, Path indexPath, String shardDataDirName) { diff --git a/server/src/test/java/org/elasticsearch/env/NodeMetadataTests.java b/server/src/test/java/org/elasticsearch/env/NodeMetadataTests.java index eccdd1c6ffea7..dab2932369c21 100644 --- a/server/src/test/java/org/elasticsearch/env/NodeMetadataTests.java +++ b/server/src/test/java/org/elasticsearch/env/NodeMetadataTests.java @@ -9,12 +9,11 @@ package org.elasticsearch.env; import org.elasticsearch.Build; +import org.elasticsearch.ElasticsearchException; import org.elasticsearch.Version; import org.elasticsearch.core.Tuple; -import org.elasticsearch.core.UpdateForV9; import org.elasticsearch.gateway.MetadataStateFormat; import org.elasticsearch.index.IndexVersion; -import org.elasticsearch.index.IndexVersions; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.EqualsHashCodeTestUtils; import org.elasticsearch.test.VersionUtils; @@ -28,6 +27,7 @@ import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.startsWith; @@ -80,22 +80,20 @@ public void testEqualsHashcodeSerialization() { ); } - @UpdateForV9(owner = UpdateForV9.Owner.CORE_INFRA) - @AwaitsFix(bugUrl = "as mentioned in the comment below, the behavior here is changing for 9.0 so this test needs updating") - public void testReadsFormatWithoutVersion() throws IOException { - // the behaviour tested here is only appropriate if the current version is compatible with versions 7 and earlier - assertTrue(IndexVersions.MINIMUM_COMPATIBLE.onOrBefore(IndexVersions.V_7_0_0)); - // when the current version is incompatible with version 7, the behaviour should change to reject files like the given resource - // which do not have the version field - + public void testFailsToReadFormatWithoutVersion() throws IOException { final Path tempDir = createTempDir(); final Path stateDir = Files.createDirectory(tempDir.resolve(MetadataStateFormat.STATE_DIR_NAME)); final InputStream resource = this.getClass().getResourceAsStream("testReadsFormatWithoutVersion.binary"); assertThat(resource, notNullValue()); Files.copy(resource, stateDir.resolve(NodeMetadata.FORMAT.getStateFileName(between(0, Integer.MAX_VALUE)))); - final NodeMetadata nodeMetadata = NodeMetadata.FORMAT.loadLatestState(logger, xContentRegistry(), tempDir); - assertThat(nodeMetadata.nodeId(), equalTo("y6VUVMSaStO4Tz-B5BxcOw")); - assertThat(nodeMetadata.nodeVersion(), equalTo(BuildVersion.fromVersionId(0))); + + ElasticsearchException ex = expectThrows( + ElasticsearchException.class, + () -> NodeMetadata.FORMAT.loadLatestState(logger, xContentRegistry(), tempDir) + ); + Throwable rootCause = ex.getRootCause(); + assertThat(rootCause, instanceOf(IllegalStateException.class)); + assertThat("Node version is required in node metadata", equalTo(rootCause.getMessage())); } public void testUpgradesLegitimateVersions() { @@ -155,11 +153,9 @@ public void testDoesNotUpgradeAncientVersion() { ); } - @UpdateForV9(owner = UpdateForV9.Owner.CORE_INFRA) - @AwaitsFix(bugUrl = "Needs to be updated for 9.0 version bump") public void testUpgradeMarksPreviousVersion() { final String nodeId = randomAlphaOfLength(10); - final Version version = VersionUtils.randomVersionBetween(random(), Version.CURRENT.minimumCompatibilityVersion(), Version.V_8_0_0); + final Version version = VersionUtils.randomVersionBetween(random(), Version.CURRENT.minimumCompatibilityVersion(), Version.V_9_0_0); final BuildVersion buildVersion = BuildVersion.fromVersionId(version.id()); final NodeMetadata nodeMetadata = new NodeMetadata(nodeId, buildVersion, IndexVersion.current()).upgradeToCurrentVersion(); diff --git a/server/src/test/java/org/elasticsearch/index/IndexVersionTests.java b/server/src/test/java/org/elasticsearch/index/IndexVersionTests.java index 8575b87c36799..f6ebd33aae9dd 100644 --- a/server/src/test/java/org/elasticsearch/index/IndexVersionTests.java +++ b/server/src/test/java/org/elasticsearch/index/IndexVersionTests.java @@ -153,6 +153,7 @@ public void testMax() { public void testGetMinimumCompatibleIndexVersion() { assertThat(IndexVersion.getMinimumCompatibleIndexVersion(7170099), equalTo(IndexVersion.fromId(6000099))); assertThat(IndexVersion.getMinimumCompatibleIndexVersion(8000099), equalTo(IndexVersion.fromId(7000099))); + assertThat(IndexVersion.getMinimumCompatibleIndexVersion(9000099), equalTo(IndexVersion.fromId(8000099))); assertThat(IndexVersion.getMinimumCompatibleIndexVersion(10000000), equalTo(IndexVersion.fromId(9000000))); } diff --git a/server/src/test/java/org/elasticsearch/index/mapper/IgnoredSourceFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/IgnoredSourceFieldMapperTests.java index 14902aa419b9f..d12bf5dc2e34c 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/IgnoredSourceFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/IgnoredSourceFieldMapperTests.java @@ -756,6 +756,23 @@ public void testIndexStoredArraySourceSingleLeafElementAndNull() throws IOExcept assertEquals("{\"value\":[\"foo\",null]}", syntheticSource); } + public void testIndexStoredArraySourceSingleLeafElementInObjectArray() throws IOException { + DocumentMapper documentMapper = createMapperServiceWithStoredArraySource(mapping(b -> { + b.startObject("path").field("synthetic_source_keep", "none").startObject("properties"); + { + b.startObject("int_value").field("type", "integer").endObject(); + } + b.endObject().endObject(); + })).documentMapper(); + var syntheticSource = syntheticSource(documentMapper, b -> { + b.startArray("path"); + b.startObject().field("int_value", 10).endObject(); + b.startObject().array("int_value", new int[] { 20 }).endObject(); + b.endArray(); + }); + assertEquals("{\"path\":{\"int_value\":[10,20]}}", syntheticSource); + } + public void testIndexStoredArraySourceSingleObjectElement() throws IOException { DocumentMapper documentMapper = createMapperServiceWithStoredArraySource(mapping(b -> { b.startObject("path").startObject("properties"); diff --git a/server/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java b/server/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java index 7d436ab5d8d22..4549a329d499a 100644 --- a/server/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java +++ b/server/src/test/java/org/elasticsearch/index/shard/IndexShardTests.java @@ -4497,7 +4497,7 @@ public void testSupplyTombstoneDoc() throws Exception { closeShards(shard); } - public void testResetEngine() throws Exception { + public void testResetEngineToGlobalCheckpoint() throws Exception { IndexShard shard = newStartedShard(false); indexOnReplicaWithGaps(shard, between(0, 1000), Math.toIntExact(shard.getLocalCheckpoint())); long maxSeqNoBeforeRollback = shard.seqNoStats().getMaxSeqNo(); @@ -4559,6 +4559,33 @@ public void testResetEngine() throws Exception { closeShard(shard, false); } + public void testResetEngine() throws Exception { + var newEngineCreated = new CountDownLatch(2); + var indexShard = newStartedShard(true, Settings.EMPTY, config -> { + try { + return new ReadOnlyEngine(config, null, null, true, Function.identity(), true, true) { + @Override + public void prepareForEngineReset() throws IOException { + ; + } + }; + } finally { + newEngineCreated.countDown(); + } + }); + var newEngineNotification = new CountDownLatch(1); + indexShard.waitForEngineOrClosedShard(ActionListener.running(newEngineNotification::countDown)); + + var onAcquired = new PlainActionFuture(); + indexShard.acquireAllPrimaryOperationsPermits(onAcquired, TimeValue.timeValueMinutes(1L)); + try (var permits = safeGet(onAcquired)) { + indexShard.resetEngine(); + } + safeAwait(newEngineCreated); + safeAwait(newEngineNotification); + closeShard(indexShard, false); + } + /** * This test simulates a scenario seen rarely in ConcurrentSeqNoVersioningIT. Closing a shard while engine is inside * resetEngineToGlobalCheckpoint can lead to check index failure in integration tests. diff --git a/server/src/test/java/org/elasticsearch/ingest/IngestCtxMapTests.java b/server/src/test/java/org/elasticsearch/ingest/IngestCtxMapTests.java index 2ae9dbfb68599..33c3ae889040b 100644 --- a/server/src/test/java/org/elasticsearch/ingest/IngestCtxMapTests.java +++ b/server/src/test/java/org/elasticsearch/ingest/IngestCtxMapTests.java @@ -110,7 +110,7 @@ public void testRemoveSource() { source.put("abc", 123); source.put("def", 456); source.put("hij", 789); - map = new IngestCtxMap(source, new TestIngestCtxMetadata(new HashMap<>(), new HashMap<>())); + map = new IngestCtxMap(source, new IngestDocMetadata(new HashMap<>(Map.of("_version", 1L)), null)); // Make sure there isn't a ConcurrentModificationException when removing a key from the iterator String removedKey = null; @@ -129,31 +129,18 @@ public void testRemoveSource() { } public void testRemove() { - String cannotRemove = "cannotRemove"; - String canRemove = "canRemove"; - Map metadata = new HashMap<>(); - metadata.put(cannotRemove, "value"); - map = new IngestCtxMap( - new HashMap<>(), - new TestIngestCtxMetadata( - metadata, - Map.of( - cannotRemove, - new Metadata.FieldProperty<>(String.class, false, true, null), - canRemove, - new Metadata.FieldProperty<>(String.class, true, true, null) - ) - ) - ); - String msg = "cannotRemove cannot be removed"; + String cannotRemove = "_version"; // writable, but not *nullable* + String canRemove = "_id"; // writable, and *nullable* + map = new IngestCtxMap(new HashMap<>(), new IngestDocMetadata(new HashMap<>(Map.of(cannotRemove, 1L)), null)); + String msg = "_version cannot be removed"; IllegalArgumentException err = expectThrows(IllegalArgumentException.class, () -> map.remove(cannotRemove)); assertEquals(msg, err.getMessage()); err = expectThrows(IllegalArgumentException.class, () -> map.put(cannotRemove, null)); - assertEquals("cannotRemove cannot be null", err.getMessage()); + assertEquals("_version cannot be null", err.getMessage()); err = expectThrows(IllegalArgumentException.class, () -> map.entrySet().iterator().next().setValue(null)); - assertEquals("cannotRemove cannot be null", err.getMessage()); + assertEquals("_version cannot be null", err.getMessage()); err = expectThrows(IllegalArgumentException.class, () -> { Iterator> it = map.entrySet().iterator(); @@ -176,6 +163,10 @@ public void testRemove() { err = expectThrows(IllegalArgumentException.class, () -> map.clear()); assertEquals(msg, err.getMessage()); + // depending on iteration order, this may have been removed, so put it back before checking the size + map.put(canRemove, "value"); + assertEquals("value", map.get(canRemove)); + assertEquals(2, map.size()); map.entrySet().remove(new TestEntry(canRemove, "value")); @@ -205,7 +196,7 @@ public void testEntryAndIterator() { source.put("foo", "bar"); source.put("baz", "qux"); source.put("noz", "zon"); - map = new IngestCtxMap(source, TestIngestCtxMetadata.withNullableVersion(metadata)); + map = new IngestCtxMap(source, new IngestDocMetadata(metadata, null)); md = map.getMetadata(); for (Map.Entry entry : map.entrySet()) { @@ -240,8 +231,10 @@ public void testEntryAndIterator() { assertTrue(map.containsKey("noz")); assertEquals(3, map.entrySet().size()); assertEquals(3, map.size()); - map.clear(); - assertEquals(0, map.size()); + + // since an IngestCtxMap must have a _version (and the _version cannot be null), we can't just .clear() + map.entrySet().removeIf(e -> e.getKey().equals("_version") == false); + assertEquals(1, map.size()); } public void testContainsValue() { @@ -336,6 +329,18 @@ public void testHandlesAllVersionTypes() { assertNull(md.getVersionType()); } + public void testGetOrDefault() { + map = new IngestCtxMap(Map.of("foo", "bar"), new IngestDocMetadata(Map.of("_version", 5L), null)); + + // it does the expected thing for fields that are present + assertThat(map.getOrDefault("_version", -1L), equalTo(5L)); + assertThat(map.getOrDefault("foo", "wat"), equalTo("bar")); + + // it does the expected thing for fields that are not present + assertThat(map.getOrDefault("_version_type", "something"), equalTo("something")); + assertThat(map.getOrDefault("baz", "quux"), equalTo("quux")); + } + private static class TestEntry implements Map.Entry { String key; Object value; diff --git a/server/src/test/java/org/elasticsearch/script/CtxMapTests.java b/server/src/test/java/org/elasticsearch/script/CtxMapTests.java index 05c6d22f2d4ec..69b98c940fd9b 100644 --- a/server/src/test/java/org/elasticsearch/script/CtxMapTests.java +++ b/server/src/test/java/org/elasticsearch/script/CtxMapTests.java @@ -17,7 +17,10 @@ import java.util.List; import java.util.Map; +import static org.elasticsearch.script.Metadata.LongField; +import static org.elasticsearch.script.Metadata.VERSION; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; public class CtxMapTests extends ESTestCase { CtxMap map; @@ -29,6 +32,37 @@ public void setUp() throws Exception { map = new CtxMap<>(new HashMap<>(), new Metadata(Map.of(), Map.of())); } + @SuppressWarnings("unchecked") + public void testGetOrDefault() { + { + map = new CtxMap<>(Map.of("foo", "bar"), new Metadata(Map.of("_version", 5L), Map.of(VERSION, LongField.withWritable()))); + + // it does the expected thing for fields that are present + assertThat(map.getOrDefault("_version", -1L), equalTo(5L)); + assertThat(((Map) map.getOrDefault("_source", Map.of())).getOrDefault("foo", "wat"), equalTo("bar")); + + // it does the expected thing for fields that are not present + assertThat(map.getOrDefault("_version_type", "something"), equalTo("something")); + assertThat(map.getOrDefault("baz", "quux"), equalTo("quux")); + } + { + map = new CtxMap<>(Map.of("foo", "bar"), new Metadata(Map.of("_version", 5L), Map.of(VERSION, LongField.withWritable()))) { + @Override + protected boolean directSourceAccess() { + return true; + } + }; + + // it does the expected thing for fields that are present + assertThat(map.getOrDefault("_version", -1L), equalTo(5L)); + assertThat(map.getOrDefault("foo", "wat"), equalTo("bar")); + + // it does the expected thing for fields that are not present + assertThat(map.getOrDefault("_version_type", "something"), equalTo("something")); + assertThat(map.getOrDefault("baz", "quux"), equalTo("quux")); + } + } + public void testAddingJunkToCtx() { IllegalArgumentException err = expectThrows(IllegalArgumentException.class, () -> map.put("junk", "stuff")); assertEquals("Cannot put key [junk] with value [stuff] into ctx", err.getMessage()); diff --git a/server/src/test/java/org/elasticsearch/script/MetadataTests.java b/server/src/test/java/org/elasticsearch/script/MetadataTests.java index 80b3dcbf16b66..7830b9d15fdd0 100644 --- a/server/src/test/java/org/elasticsearch/script/MetadataTests.java +++ b/server/src/test/java/org/elasticsearch/script/MetadataTests.java @@ -16,6 +16,8 @@ import java.util.Map; import java.util.Set; +import static org.hamcrest.Matchers.equalTo; + public class MetadataTests extends ESTestCase { Metadata md; private static final Metadata.FieldProperty STRING_PROP = new Metadata.FieldProperty<>(String.class, true, true, null); @@ -279,4 +281,11 @@ public void testImmutablePropertiesMap() { new Metadata(Map.of(), Map.of()); new Metadata(Map.of(), Map.copyOf(new HashMap<>())); } + + public void testGetOrDefault() { + md = new Metadata(new HashMap<>(Map.of("foo", "bar")), Map.of("foo", STRING_PROP, "baz", STRING_PROP)); + assertThat(md.getOrDefault("foo", "wat"), equalTo("bar")); + assertThat(md.getOrDefault("bar", "wat"), equalTo("wat")); + assertThat(md.getOrDefault("yo", "wat"), equalTo("wat")); + } } diff --git a/server/src/test/java/org/elasticsearch/transport/TransportHandshakerRawMessageTests.java b/server/src/test/java/org/elasticsearch/transport/TransportHandshakerRawMessageTests.java new file mode 100644 index 0000000000000..de44ca70f2005 --- /dev/null +++ b/server/src/test/java/org/elasticsearch/transport/TransportHandshakerRawMessageTests.java @@ -0,0 +1,251 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +package org.elasticsearch.transport; + +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.Build; +import org.elasticsearch.TransportVersion; +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.cluster.node.DiscoveryNodeUtils; +import org.elasticsearch.common.bytes.BytesArray; +import org.elasticsearch.common.io.stream.BytesStreamOutput; +import org.elasticsearch.common.io.stream.InputStreamStreamInput; +import org.elasticsearch.common.io.stream.OutputStreamStreamOutput; +import org.elasticsearch.common.transport.TransportAddress; +import org.elasticsearch.core.UpdateForV10; +import org.elasticsearch.core.UpdateForV9; +import org.elasticsearch.test.ESSingleNodeTestCase; +import org.elasticsearch.test.TransportVersionUtils; + +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.nio.charset.StandardCharsets; +import java.security.AccessController; +import java.security.PrivilegedExceptionAction; + +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.hamcrest.Matchers.lessThan; + +public class TransportHandshakerRawMessageTests extends ESSingleNodeTestCase { + + @UpdateForV9(owner = UpdateForV9.Owner.CORE_INFRA) // remove support for v7 handshakes in v9 + public void testV7Handshake() throws Exception { + final BytesRef handshakeRequestBytes; + final var requestId = randomNonNegativeLong(); + try (var outputStream = new BytesStreamOutput()) { + outputStream.setTransportVersion(TransportHandshaker.V7_HANDSHAKE_VERSION); + outputStream.writeLong(requestId); + outputStream.writeByte(TransportStatus.setRequest(TransportStatus.setHandshake((byte) 0))); + outputStream.writeInt(TransportHandshaker.V7_HANDSHAKE_VERSION.id()); + outputStream.writeByte((byte) 0); // no request headers; + outputStream.writeByte((byte) 0); // no response headers; + outputStream.writeStringArray(new String[] { "x-pack" }); // one feature + outputStream.writeString("internal:tcp/handshake"); + outputStream.writeByte((byte) 0); // no parent task ID; + + final var requestNodeTransportVersionId = TransportVersionUtils.randomCompatibleVersion(random()).id(); + assertThat(requestNodeTransportVersionId, allOf(greaterThanOrEqualTo(1 << 22), lessThan(1 << 28))); // 4-byte vInt + outputStream.writeByte((byte) 4); // payload length + outputStream.writeVInt(requestNodeTransportVersionId); + + handshakeRequestBytes = outputStream.bytes().toBytesRef(); + } + + final BytesRef handshakeResponseBytes; + try (var socket = openTransportConnection()) { + var streamOutput = new OutputStreamStreamOutput(socket.getOutputStream()); + streamOutput.write("ES".getBytes(StandardCharsets.US_ASCII)); + streamOutput.writeInt(handshakeRequestBytes.length); + streamOutput.writeBytes(handshakeRequestBytes.bytes, handshakeRequestBytes.offset, handshakeRequestBytes.length); + streamOutput.flush(); + + var streamInput = new InputStreamStreamInput(socket.getInputStream()); + assertEquals((byte) 'E', streamInput.readByte()); + assertEquals((byte) 'S', streamInput.readByte()); + var responseLength = streamInput.readInt(); + handshakeResponseBytes = streamInput.readBytesRef(responseLength); + } + + try (var inputStream = new BytesArray(handshakeResponseBytes).streamInput()) { + assertEquals(requestId, inputStream.readLong()); + assertEquals(TransportStatus.setResponse(TransportStatus.setHandshake((byte) 0)), inputStream.readByte()); + assertEquals(TransportHandshaker.V7_HANDSHAKE_VERSION.id(), inputStream.readInt()); + assertEquals((byte) 0, inputStream.readByte()); // no request headers + assertEquals((byte) 0, inputStream.readByte()); // no response headers + inputStream.setTransportVersion(TransportHandshaker.V7_HANDSHAKE_VERSION); + assertEquals(TransportVersion.current().id(), inputStream.readVInt()); + assertEquals(-1, inputStream.read()); + } + } + + @UpdateForV10(owner = UpdateForV10.Owner.CORE_INFRA) // remove support for v8 handshakes in v10 + public void testV8Handshake() throws Exception { + final BytesRef handshakeRequestBytes; + final var requestId = randomNonNegativeLong(); + try (var outputStream = new BytesStreamOutput()) { + outputStream.setTransportVersion(TransportHandshaker.V8_HANDSHAKE_VERSION); + outputStream.writeLong(requestId); + outputStream.writeByte(TransportStatus.setRequest(TransportStatus.setHandshake((byte) 0))); + outputStream.writeInt(TransportHandshaker.V8_HANDSHAKE_VERSION.id()); + outputStream.writeInt(0x1a); // length of variable-length header, always 0x1a + outputStream.writeByte((byte) 0); // no request headers; + outputStream.writeByte((byte) 0); // no response headers; + outputStream.writeByte((byte) 0); // no features; + outputStream.writeString("internal:tcp/handshake"); + outputStream.writeByte((byte) 0); // no parent task ID; + + final var requestNodeTransportVersionId = TransportVersionUtils.randomCompatibleVersion(random()).id(); + assertThat(requestNodeTransportVersionId, allOf(greaterThanOrEqualTo(1 << 22), lessThan(1 << 28))); // 4-byte vInt + outputStream.writeByte((byte) 4); // payload length + outputStream.writeVInt(requestNodeTransportVersionId); + + handshakeRequestBytes = outputStream.bytes().toBytesRef(); + } + + final BytesRef handshakeResponseBytes; + try (var socket = openTransportConnection()) { + var streamOutput = new OutputStreamStreamOutput(socket.getOutputStream()); + streamOutput.write("ES".getBytes(StandardCharsets.US_ASCII)); + streamOutput.writeInt(handshakeRequestBytes.length); + streamOutput.writeBytes(handshakeRequestBytes.bytes, handshakeRequestBytes.offset, handshakeRequestBytes.length); + streamOutput.flush(); + + var streamInput = new InputStreamStreamInput(socket.getInputStream()); + assertEquals((byte) 'E', streamInput.readByte()); + assertEquals((byte) 'S', streamInput.readByte()); + var responseLength = streamInput.readInt(); + handshakeResponseBytes = streamInput.readBytesRef(responseLength); + } + + try (var inputStream = new BytesArray(handshakeResponseBytes).streamInput()) { + assertEquals(requestId, inputStream.readLong()); + assertEquals(TransportStatus.setResponse(TransportStatus.setHandshake((byte) 0)), inputStream.readByte()); + assertEquals(TransportHandshaker.V8_HANDSHAKE_VERSION.id(), inputStream.readInt()); + assertEquals(2, inputStream.readInt()); // length of variable-length header, always 0x02 + assertEquals((byte) 0, inputStream.readByte()); // no request headers + assertEquals((byte) 0, inputStream.readByte()); // no response headers + inputStream.setTransportVersion(TransportHandshaker.V8_HANDSHAKE_VERSION); + assertEquals(TransportVersion.current().id(), inputStream.readVInt()); + assertEquals(-1, inputStream.read()); + } + } + + @UpdateForV10(owner = UpdateForV10.Owner.CORE_INFRA) // remove support for v9 handshakes in v11 + public void testV9Handshake() throws Exception { + final BytesRef handshakeRequestBytes; + final var requestId = randomNonNegativeLong(); + try (var outputStream = new BytesStreamOutput()) { + outputStream.setTransportVersion(TransportHandshaker.V9_HANDSHAKE_VERSION); + outputStream.writeLong(requestId); + outputStream.writeByte(TransportStatus.setRequest(TransportStatus.setHandshake((byte) 0))); + outputStream.writeInt(TransportHandshaker.V9_HANDSHAKE_VERSION.id()); + outputStream.writeInt(0x19); // length of variable-length header, always 0x19 + outputStream.writeByte((byte) 0); // no request headers; + outputStream.writeByte((byte) 0); // no response headers; + outputStream.writeString("internal:tcp/handshake"); + outputStream.writeByte((byte) 0); // no parent task ID; + + final var requestNodeTransportVersionId = TransportVersionUtils.randomCompatibleVersion(random()).id(); + assertThat(requestNodeTransportVersionId, allOf(greaterThanOrEqualTo(1 << 22), lessThan(1 << 28))); // 4-byte vInt + final var releaseVersionLength = between(0, 127 - 5); // so that its length, and the length of the payload, is a one-byte vInt + final var requestNodeReleaseVersion = randomAlphaOfLength(releaseVersionLength); + outputStream.writeByte((byte) (4 + 1 + releaseVersionLength)); // payload length + outputStream.writeVInt(requestNodeTransportVersionId); + outputStream.writeString(requestNodeReleaseVersion); + + handshakeRequestBytes = outputStream.bytes().toBytesRef(); + } + + final BytesRef handshakeResponseBytes; + try (var socket = openTransportConnection()) { + var streamOutput = new OutputStreamStreamOutput(socket.getOutputStream()); + streamOutput.write("ES".getBytes(StandardCharsets.US_ASCII)); + streamOutput.writeInt(handshakeRequestBytes.length); + streamOutput.writeBytes(handshakeRequestBytes.bytes, handshakeRequestBytes.offset, handshakeRequestBytes.length); + streamOutput.flush(); + + var streamInput = new InputStreamStreamInput(socket.getInputStream()); + assertEquals((byte) 'E', streamInput.readByte()); + assertEquals((byte) 'S', streamInput.readByte()); + var responseLength = streamInput.readInt(); + handshakeResponseBytes = streamInput.readBytesRef(responseLength); + } + + try (var inputStream = new BytesArray(handshakeResponseBytes).streamInput()) { + assertEquals(requestId, inputStream.readLong()); + assertEquals(TransportStatus.setResponse(TransportStatus.setHandshake((byte) 0)), inputStream.readByte()); + assertEquals(TransportHandshaker.V9_HANDSHAKE_VERSION.id(), inputStream.readInt()); + assertEquals(2, inputStream.readInt()); // length of variable-length header, always 0x02 + assertEquals((byte) 0, inputStream.readByte()); // no request headers + assertEquals((byte) 0, inputStream.readByte()); // no response headers + inputStream.setTransportVersion(TransportHandshaker.V9_HANDSHAKE_VERSION); + assertEquals(TransportVersion.current().id(), inputStream.readVInt()); + assertEquals(Build.current().version(), inputStream.readString()); + assertEquals(-1, inputStream.read()); + } + } + + public void testOutboundHandshake() throws Exception { + final BytesRef handshakeRequestBytes; + + try (var serverSocket = new ServerSocket(0, 1, InetAddress.getLoopbackAddress())) { + getInstanceFromNode(TransportService.class).openConnection( + DiscoveryNodeUtils.builder(randomIdentifier()) + .address(new TransportAddress(serverSocket.getInetAddress(), serverSocket.getLocalPort())) + .build(), + ConnectionProfile.buildSingleChannelProfile(TransportRequestOptions.Type.REG, null, null, null, null, null), + ActionListener.noop() + ); + + try ( + var acceptedSocket = serverSocket.accept(); + var streamInput = new InputStreamStreamInput(acceptedSocket.getInputStream()) + ) { + assertEquals((byte) 'E', streamInput.readByte()); + assertEquals((byte) 'S', streamInput.readByte()); + var responseLength = streamInput.readInt(); + handshakeRequestBytes = streamInput.readBytesRef(responseLength); + } + } + + final BytesRef payloadBytes; + + try (var inputStream = new BytesArray(handshakeRequestBytes).streamInput()) { + assertThat(inputStream.readLong(), greaterThan(0L)); + assertEquals(TransportStatus.setRequest(TransportStatus.setHandshake((byte) 0)), inputStream.readByte()); + assertEquals(TransportHandshaker.V8_HANDSHAKE_VERSION.id(), inputStream.readInt()); + assertEquals(0x1a, inputStream.readInt()); // length of variable-length header, always 0x1a + assertEquals((byte) 0, inputStream.readByte()); // no request headers + assertEquals((byte) 0, inputStream.readByte()); // no response headers + assertEquals((byte) 0, inputStream.readByte()); // no features + assertEquals("internal:tcp/handshake", inputStream.readString()); + assertEquals((byte) 0, inputStream.readByte()); // no parent task + inputStream.setTransportVersion(TransportHandshaker.V8_HANDSHAKE_VERSION); + payloadBytes = inputStream.readBytesRef(); + assertEquals(-1, inputStream.read()); + } + + try (var inputStream = new BytesArray(payloadBytes).streamInput()) { + inputStream.setTransportVersion(TransportHandshaker.V8_HANDSHAKE_VERSION); + assertEquals(TransportVersion.current().id(), inputStream.readVInt()); + assertEquals(-1, inputStream.read()); + } + } + + private Socket openTransportConnection() throws Exception { + final var transportAddress = randomFrom(getInstanceFromNode(TransportService.class).boundAddress().boundAddresses()).address(); + return AccessController.doPrivileged( + (PrivilegedExceptionAction) (() -> new Socket(transportAddress.getAddress(), transportAddress.getPort())) + ); + } +} diff --git a/test/framework/src/main/java/org/elasticsearch/index/mapper/MapperTestCase.java b/test/framework/src/main/java/org/elasticsearch/index/mapper/MapperTestCase.java index a62af5729a096..809660c5e9af8 100644 --- a/test/framework/src/main/java/org/elasticsearch/index/mapper/MapperTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/index/mapper/MapperTestCase.java @@ -105,8 +105,6 @@ */ public abstract class MapperTestCase extends MapperServiceTestCase { - public static final IndexVersion DEPRECATED_BOOST_INDEX_VERSION = IndexVersions.V_7_10_0; - protected abstract void minimalMapping(XContentBuilder b) throws IOException; /** diff --git a/test/framework/src/main/java/org/elasticsearch/ingest/TestIngestCtxMetadata.java b/test/framework/src/main/java/org/elasticsearch/ingest/TestIngestCtxMetadata.java deleted file mode 100644 index 7dccecbde6a08..0000000000000 --- a/test/framework/src/main/java/org/elasticsearch/ingest/TestIngestCtxMetadata.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -package org.elasticsearch.ingest; - -import org.elasticsearch.script.Metadata; - -import java.util.HashMap; -import java.util.Map; - -public class TestIngestCtxMetadata extends IngestDocMetadata { - public TestIngestCtxMetadata(Map map, Map> properties) { - super(map, Map.copyOf(properties), null); - } - - public static TestIngestCtxMetadata withNullableVersion(Map map) { - Map> updatedProperties = new HashMap<>(IngestDocMetadata.PROPERTIES); - updatedProperties.replace(VERSION, new Metadata.FieldProperty<>(Number.class, true, true, FieldProperty.LONGABLE_NUMBER)); - return new TestIngestCtxMetadata(map, updatedProperties); - } -} diff --git a/test/framework/src/main/java/org/elasticsearch/ingest/TestIngestDocument.java b/test/framework/src/main/java/org/elasticsearch/ingest/TestIngestDocument.java index 798c824d3be9b..aa1833a659da3 100644 --- a/test/framework/src/main/java/org/elasticsearch/ingest/TestIngestDocument.java +++ b/test/framework/src/main/java/org/elasticsearch/ingest/TestIngestDocument.java @@ -10,10 +10,8 @@ package org.elasticsearch.ingest; import org.elasticsearch.common.lucene.uid.Versions; -import org.elasticsearch.common.util.Maps; import org.elasticsearch.core.Tuple; import org.elasticsearch.index.VersionType; -import org.elasticsearch.script.Metadata; import org.elasticsearch.test.ESTestCase; import java.util.HashMap; @@ -24,53 +22,30 @@ */ public class TestIngestDocument { public static final long DEFAULT_VERSION = 12345L; - private static String VERSION = IngestDocument.Metadata.VERSION.getFieldName(); - - /** - * Create an IngestDocument for testing that pass an empty mutable map for ingestMetaata - */ - public static IngestDocument withNullableVersion(Map sourceAndMetadata) { - return ofIngestWithNullableVersion(sourceAndMetadata, new HashMap<>()); - } + private static final String VERSION = IngestDocument.Metadata.VERSION.getFieldName(); /** * Create an {@link IngestDocument} from the given sourceAndMetadata and ingestMetadata and a version validator that allows null * _versions. Normally null _version is not allowed, but many tests don't care about that invariant. */ - public static IngestDocument ofIngestWithNullableVersion(Map sourceAndMetadata, Map ingestMetadata) { - Map source = new HashMap<>(sourceAndMetadata); - Map metadata = Maps.newHashMapWithExpectedSize(IngestDocument.Metadata.values().length); - for (IngestDocument.Metadata m : IngestDocument.Metadata.values()) { - String key = m.getFieldName(); - if (sourceAndMetadata.containsKey(key)) { - metadata.put(key, source.remove(key)); - } - } - return new IngestDocument(new IngestCtxMap(source, TestIngestCtxMetadata.withNullableVersion(metadata)), ingestMetadata); - } - - /** - * Create an {@link IngestDocument} with {@link #DEFAULT_VERSION} as the _version metadata, if _version is not already present. - */ - public static IngestDocument withDefaultVersion(Map sourceAndMetadata) { + public static IngestDocument withDefaultVersion(Map sourceAndMetadata, Map ingestMetadata) { if (sourceAndMetadata.containsKey(VERSION) == false) { sourceAndMetadata = new HashMap<>(sourceAndMetadata); sourceAndMetadata.put(VERSION, DEFAULT_VERSION); } - return new IngestDocument(sourceAndMetadata, new HashMap<>()); + return new IngestDocument(sourceAndMetadata, ingestMetadata); } /** - * Create an IngestDocument with a metadata map and validators. The metadata map is passed by reference, not copied, so callers - * can observe changes to the map directly. + * Create an {@link IngestDocument} with {@link #DEFAULT_VERSION} as the _version metadata, if _version is not already present. */ - public static IngestDocument ofMetadataWithValidator(Map metadata, Map> properties) { - return new IngestDocument(new IngestCtxMap(new HashMap<>(), new TestIngestCtxMetadata(metadata, properties)), new HashMap<>()); + public static IngestDocument withDefaultVersion(Map sourceAndMetadata) { + return withDefaultVersion(sourceAndMetadata, new HashMap<>()); } /** * Create an empty ingest document for testing. - * + *

* Adds the required {@code "_version"} metadata key with value {@link #DEFAULT_VERSION}. */ public static IngestDocument emptyIngestDocument() { diff --git a/test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/FieldType.java b/test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/FieldType.java index 96b75f29382e2..13c802fcd5809 100644 --- a/test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/FieldType.java +++ b/test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/FieldType.java @@ -11,13 +11,9 @@ import org.elasticsearch.logsdb.datageneration.datasource.DataSource; import org.elasticsearch.logsdb.datageneration.fields.leaf.ByteFieldDataGenerator; -import org.elasticsearch.logsdb.datageneration.fields.leaf.DoubleFieldDataGenerator; -import org.elasticsearch.logsdb.datageneration.fields.leaf.FloatFieldDataGenerator; -import org.elasticsearch.logsdb.datageneration.fields.leaf.HalfFloatFieldDataGenerator; import org.elasticsearch.logsdb.datageneration.fields.leaf.IntegerFieldDataGenerator; import org.elasticsearch.logsdb.datageneration.fields.leaf.KeywordFieldDataGenerator; import org.elasticsearch.logsdb.datageneration.fields.leaf.LongFieldDataGenerator; -import org.elasticsearch.logsdb.datageneration.fields.leaf.ScaledFloatFieldDataGenerator; import org.elasticsearch.logsdb.datageneration.fields.leaf.ShortFieldDataGenerator; import org.elasticsearch.logsdb.datageneration.fields.leaf.UnsignedLongFieldDataGenerator; @@ -30,11 +26,7 @@ public enum FieldType { UNSIGNED_LONG("unsigned_long"), INTEGER("integer"), SHORT("short"), - BYTE("byte"), - DOUBLE("double"), - FLOAT("float"), - HALF_FLOAT("half_float"), - SCALED_FLOAT("scaled_float"); + BYTE("byte"); private final String name; @@ -50,10 +42,6 @@ public FieldDataGenerator generator(String fieldName, DataSource dataSource) { case INTEGER -> new IntegerFieldDataGenerator(fieldName, dataSource); case SHORT -> new ShortFieldDataGenerator(fieldName, dataSource); case BYTE -> new ByteFieldDataGenerator(fieldName, dataSource); - case DOUBLE -> new DoubleFieldDataGenerator(fieldName, dataSource); - case FLOAT -> new FloatFieldDataGenerator(fieldName, dataSource); - case HALF_FLOAT -> new HalfFloatFieldDataGenerator(fieldName, dataSource); - case SCALED_FLOAT -> new ScaledFloatFieldDataGenerator(fieldName, dataSource); }; } diff --git a/test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/datasource/DefaultMappingParametersHandler.java b/test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/datasource/DefaultMappingParametersHandler.java index b639108ea6ad2..832a3205cfcb9 100644 --- a/test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/datasource/DefaultMappingParametersHandler.java +++ b/test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/datasource/DefaultMappingParametersHandler.java @@ -32,8 +32,8 @@ public DataSourceResponse.LeafMappingParametersGenerator handle(DataSourceReques return new DataSourceResponse.LeafMappingParametersGenerator(switch (request.fieldType()) { case KEYWORD -> keywordMapping(request, map); - case LONG, INTEGER, SHORT, BYTE, DOUBLE, FLOAT, HALF_FLOAT, UNSIGNED_LONG -> plain(map); - case SCALED_FLOAT -> scaledFloatMapping(map); + case LONG, INTEGER, SHORT, BYTE, UNSIGNED_LONG -> plain(map); + }); } @@ -61,7 +61,8 @@ private Supplier> keywordMapping( .collect(Collectors.toSet()); if (options.isEmpty() == false) { - injected.put("copy_to", ESTestCase.randomFrom(options)); + // TODO: re-enable once #120831 is resolved + // injected.put("copy_to", ESTestCase.randomFrom(options)); } } diff --git a/x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/matchers/ArrayEqualMatcher.java b/test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/matchers/ArrayEqualMatcher.java similarity index 83% rename from x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/matchers/ArrayEqualMatcher.java rename to test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/matchers/ArrayEqualMatcher.java index b98ad65ac4d4f..940bb1b7553fc 100644 --- a/x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/matchers/ArrayEqualMatcher.java +++ b/test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/matchers/ArrayEqualMatcher.java @@ -1,11 +1,13 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ -package org.elasticsearch.xpack.logsdb.qa.matchers; +package org.elasticsearch.logsdb.datageneration.matchers; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.xcontent.XContentBuilder; @@ -13,8 +15,8 @@ import java.util.Arrays; import java.util.List; -import static org.elasticsearch.xpack.logsdb.qa.matchers.Messages.formatErrorMessage; -import static org.elasticsearch.xpack.logsdb.qa.matchers.Messages.prettyPrintArrays; +import static org.elasticsearch.logsdb.datageneration.matchers.Messages.formatErrorMessage; +import static org.elasticsearch.logsdb.datageneration.matchers.Messages.prettyPrintArrays; class ArrayEqualMatcher extends GenericEqualsMatcher { ArrayEqualMatcher( diff --git a/x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/matchers/GenericEqualsMatcher.java b/test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/matchers/GenericEqualsMatcher.java similarity index 85% rename from x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/matchers/GenericEqualsMatcher.java rename to test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/matchers/GenericEqualsMatcher.java index 933c7eb86f65a..b409d18709f23 100644 --- a/x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/matchers/GenericEqualsMatcher.java +++ b/test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/matchers/GenericEqualsMatcher.java @@ -1,18 +1,20 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ -package org.elasticsearch.xpack.logsdb.qa.matchers; +package org.elasticsearch.logsdb.datageneration.matchers; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.xcontent.XContentBuilder; import java.util.List; -import static org.elasticsearch.xpack.logsdb.qa.matchers.Messages.formatErrorMessage; +import static org.elasticsearch.logsdb.datageneration.matchers.Messages.formatErrorMessage; public class GenericEqualsMatcher extends Matcher { protected final XContentBuilder actualMappings; diff --git a/x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/matchers/ListEqualMatcher.java b/test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/matchers/ListEqualMatcher.java similarity index 83% rename from x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/matchers/ListEqualMatcher.java rename to test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/matchers/ListEqualMatcher.java index 447aa21b932c2..093556ea89355 100644 --- a/x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/matchers/ListEqualMatcher.java +++ b/test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/matchers/ListEqualMatcher.java @@ -1,19 +1,21 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ -package org.elasticsearch.xpack.logsdb.qa.matchers; +package org.elasticsearch.logsdb.datageneration.matchers; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.xcontent.XContentBuilder; import java.util.List; -import static org.elasticsearch.xpack.logsdb.qa.matchers.Messages.formatErrorMessage; -import static org.elasticsearch.xpack.logsdb.qa.matchers.Messages.prettyPrintCollections; +import static org.elasticsearch.logsdb.datageneration.matchers.Messages.formatErrorMessage; +import static org.elasticsearch.logsdb.datageneration.matchers.Messages.prettyPrintCollections; public class ListEqualMatcher extends GenericEqualsMatcher> { public ListEqualMatcher( diff --git a/x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/matchers/MatchResult.java b/test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/matchers/MatchResult.java similarity index 73% rename from x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/matchers/MatchResult.java rename to test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/matchers/MatchResult.java index a890a0375ef03..77b51890ad691 100644 --- a/x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/matchers/MatchResult.java +++ b/test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/matchers/MatchResult.java @@ -1,11 +1,13 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ -package org.elasticsearch.xpack.logsdb.qa.matchers; +package org.elasticsearch.logsdb.datageneration.matchers; import java.util.Objects; diff --git a/x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/matchers/Matcher.java b/test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/matchers/Matcher.java similarity index 90% rename from x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/matchers/Matcher.java rename to test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/matchers/Matcher.java index e08e401c19530..dd87e23351c0d 100644 --- a/x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/matchers/Matcher.java +++ b/test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/matchers/Matcher.java @@ -1,15 +1,17 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ -package org.elasticsearch.xpack.logsdb.qa.matchers; +package org.elasticsearch.logsdb.datageneration.matchers; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.logsdb.datageneration.matchers.source.SourceMatcher; import org.elasticsearch.xcontent.XContentBuilder; -import org.elasticsearch.xpack.logsdb.qa.matchers.source.SourceMatcher; import java.util.List; import java.util.Map; diff --git a/x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/matchers/Messages.java b/test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/matchers/Messages.java similarity index 80% rename from x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/matchers/Messages.java rename to test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/matchers/Messages.java index 122e3b2d6261c..6f7ccc69c35e1 100644 --- a/x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/matchers/Messages.java +++ b/test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/matchers/Messages.java @@ -1,11 +1,13 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ -package org.elasticsearch.xpack.logsdb.qa.matchers; +package org.elasticsearch.logsdb.datageneration.matchers; import org.elasticsearch.common.Strings; import org.elasticsearch.common.settings.Settings; diff --git a/x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/matchers/ObjectMatcher.java b/test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/matchers/ObjectMatcher.java similarity index 68% rename from x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/matchers/ObjectMatcher.java rename to test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/matchers/ObjectMatcher.java index f2f08b1dfac14..f922385fc0190 100644 --- a/x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/matchers/ObjectMatcher.java +++ b/test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/matchers/ObjectMatcher.java @@ -1,16 +1,18 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ -package org.elasticsearch.xpack.logsdb.qa.matchers; +package org.elasticsearch.logsdb.datageneration.matchers; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.xcontent.XContentBuilder; -import static org.elasticsearch.xpack.logsdb.qa.matchers.Messages.formatErrorMessage; +import static org.elasticsearch.logsdb.datageneration.matchers.Messages.formatErrorMessage; public class ObjectMatcher extends GenericEqualsMatcher { ObjectMatcher( diff --git a/x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/matchers/source/DynamicFieldMatcher.java b/test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/matchers/source/DynamicFieldMatcher.java similarity index 80% rename from x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/matchers/source/DynamicFieldMatcher.java rename to test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/matchers/source/DynamicFieldMatcher.java index d6812c41f7611..5bcf53cfa5c9a 100644 --- a/x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/matchers/source/DynamicFieldMatcher.java +++ b/test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/matchers/source/DynamicFieldMatcher.java @@ -1,15 +1,17 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ -package org.elasticsearch.xpack.logsdb.qa.matchers.source; +package org.elasticsearch.logsdb.datageneration.matchers.source; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.logsdb.datageneration.matchers.MatchResult; import org.elasticsearch.xcontent.XContentBuilder; -import org.elasticsearch.xpack.logsdb.qa.matchers.MatchResult; import java.util.List; import java.util.Objects; @@ -18,8 +20,8 @@ import java.util.function.Function; import java.util.stream.Collectors; -import static org.elasticsearch.xpack.logsdb.qa.matchers.Messages.formatErrorMessage; -import static org.elasticsearch.xpack.logsdb.qa.matchers.Messages.prettyPrintCollections; +import static org.elasticsearch.logsdb.datageneration.matchers.Messages.formatErrorMessage; +import static org.elasticsearch.logsdb.datageneration.matchers.Messages.prettyPrintCollections; class DynamicFieldMatcher { private final XContentBuilder actualMappings; diff --git a/x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/matchers/source/FieldSpecificMatcher.java b/test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/matchers/source/FieldSpecificMatcher.java similarity index 73% rename from x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/matchers/source/FieldSpecificMatcher.java rename to test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/matchers/source/FieldSpecificMatcher.java index 0c970f1b5fd9a..960cc38e55c82 100644 --- a/x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/matchers/source/FieldSpecificMatcher.java +++ b/test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/matchers/source/FieldSpecificMatcher.java @@ -1,16 +1,18 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ -package org.elasticsearch.xpack.logsdb.qa.matchers.source; +package org.elasticsearch.logsdb.datageneration.matchers.source; import org.apache.lucene.sandbox.document.HalfFloatPoint; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.logsdb.datageneration.matchers.MatchResult; import org.elasticsearch.xcontent.XContentBuilder; -import org.elasticsearch.xpack.logsdb.qa.matchers.MatchResult; import java.math.BigInteger; import java.util.List; @@ -20,8 +22,8 @@ import java.util.function.Function; import java.util.stream.Collectors; -import static org.elasticsearch.xpack.logsdb.qa.matchers.Messages.formatErrorMessage; -import static org.elasticsearch.xpack.logsdb.qa.matchers.Messages.prettyPrintCollections; +import static org.elasticsearch.logsdb.datageneration.matchers.Messages.formatErrorMessage; +import static org.elasticsearch.logsdb.datageneration.matchers.Messages.prettyPrintCollections; interface FieldSpecificMatcher { MatchResult match(List actual, List expected, Map actualMapping, Map expectedMapping); @@ -115,40 +117,39 @@ public MatchResult match( } assert scalingFactor instanceof Number; - var expectedNormalized = normalizeExpected(expected, ((Number) scalingFactor).doubleValue()); - var actualNormalized = normalizeActual(actual); + double scalingFactorDouble = ((Number) scalingFactor).doubleValue(); + // It is possible that we receive a mix of reduced precision values and original values. + // F.e. in case of `synthetic_source_keep: "arrays"` in nested objects only arrays are preserved as is + // and therefore any singleton values have reduced precision. + // Therefore, we need to match either an exact value or a normalized value. + var expectedNormalized = normalizeValues(expected); + var actualNormalized = normalizeValues(actual); + for (var expectedValue : expectedNormalized) { + if (actualNormalized.contains(expectedValue) == false + && actualNormalized.contains(encodeDecodeWithPrecisionLoss(expectedValue, scalingFactorDouble)) == false) { + return MatchResult.noMatch( + formatErrorMessage( + actualMappings, + actualSettings, + expectedMappings, + expectedSettings, + "Values of type [scaled_float] don't match after normalization, normalized " + + prettyPrintCollections(actualNormalized, expectedNormalized) + ) + ); + } + } - return actualNormalized.equals(expectedNormalized) - ? MatchResult.match() - : MatchResult.noMatch( - formatErrorMessage( - actualMappings, - actualSettings, - expectedMappings, - expectedSettings, - "Values of type [scaled_float] don't match after normalization, normalized " - + prettyPrintCollections(actualNormalized, expectedNormalized) - ) - ); + return MatchResult.match(); } - private static Set normalizeExpected(List values, double scalingFactor) { - if (values == null) { - return Set.of(); - } - - return values.stream() - .filter(Objects::nonNull) - .map(ScaledFloatMatcher::toDouble) - // Based on logic in ScaledFloatFieldMapper - .map(v -> { - var encoded = Math.round(v * scalingFactor); - return encoded / scalingFactor; - }) - .collect(Collectors.toSet()); + private Double encodeDecodeWithPrecisionLoss(double value, double scalingFactor) { + // Based on logic in ScaledFloatFieldMapper + var encoded = Math.round(value * scalingFactor); + return encoded / scalingFactor; } - private static Set normalizeActual(List values) { + private static Set normalizeValues(List values) { if (values == null) { return Set.of(); } diff --git a/x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/matchers/source/MappingTransforms.java b/test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/matchers/source/MappingTransforms.java similarity index 88% rename from x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/matchers/source/MappingTransforms.java rename to test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/matchers/source/MappingTransforms.java index dbe73e3c2a4c2..312be273dcd3e 100644 --- a/x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/matchers/source/MappingTransforms.java +++ b/test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/matchers/source/MappingTransforms.java @@ -1,11 +1,13 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ -package org.elasticsearch.xpack.logsdb.qa.matchers.source; +package org.elasticsearch.logsdb.datageneration.matchers.source; import java.util.ArrayList; import java.util.HashMap; diff --git a/x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/matchers/source/SourceMatcher.java b/test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/matchers/source/SourceMatcher.java similarity index 90% rename from x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/matchers/source/SourceMatcher.java rename to test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/matchers/source/SourceMatcher.java index cd2bb361d065d..eb62598712f03 100644 --- a/x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/matchers/source/SourceMatcher.java +++ b/test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/matchers/source/SourceMatcher.java @@ -1,27 +1,29 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". */ -package org.elasticsearch.xpack.logsdb.qa.matchers.source; +package org.elasticsearch.logsdb.datageneration.matchers.source; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentHelper; +import org.elasticsearch.logsdb.datageneration.matchers.GenericEqualsMatcher; +import org.elasticsearch.logsdb.datageneration.matchers.ListEqualMatcher; +import org.elasticsearch.logsdb.datageneration.matchers.MatchResult; import org.elasticsearch.xcontent.XContentBuilder; -import org.elasticsearch.xpack.logsdb.qa.matchers.GenericEqualsMatcher; -import org.elasticsearch.xpack.logsdb.qa.matchers.ListEqualMatcher; -import org.elasticsearch.xpack.logsdb.qa.matchers.MatchResult; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; -import static org.elasticsearch.xpack.logsdb.qa.matchers.Messages.formatErrorMessage; -import static org.elasticsearch.xpack.logsdb.qa.matchers.Messages.prettyPrintCollections; +import static org.elasticsearch.logsdb.datageneration.matchers.Messages.formatErrorMessage; +import static org.elasticsearch.logsdb.datageneration.matchers.Messages.prettyPrintCollections; public class SourceMatcher extends GenericEqualsMatcher>> { private final Map actualNormalizedMapping; @@ -104,7 +106,7 @@ private MatchResult compareSource(Map> actual, Map> actual, Map masterStateMap = convertToMap(masterClusterState); - int masterClusterStateSize = ClusterState.Builder.toBytes(masterClusterState).length; String masterId = masterClusterState.nodes().getMasterNodeId(); for (SubscribableListener localStateListener : localStates) { localStateListener.andThenAccept(localClusterStateResponse -> { @@ -1255,7 +1254,6 @@ protected final void doEnsureClusterStateConsistency(NamedWriteableRegistry name namedWriteableRegistry ); final Map localStateMap = convertToMap(localClusterState); - final int localClusterStateSize = ClusterState.Builder.toBytes(localClusterState).length; // Check that the non-master node has the same version of the cluster state as the master and // that the master node matches the master (otherwise there is no requirement for the cluster state to // match) @@ -1267,9 +1265,10 @@ protected final void doEnsureClusterStateConsistency(NamedWriteableRegistry name masterClusterState.stateUUID(), localClusterState.stateUUID() ); - // We cannot compare serialization bytes since serialization order of maps is not guaranteed - // but we can compare serialization sizes - they should be the same - assertEquals("cluster state size does not match", masterClusterStateSize, localClusterStateSize); + + // Compare the stateMaps for equality. + assertNull(XContentTestUtils.differenceBetweenMapsIgnoringArrayOrder(masterStateMap, localStateMap)); + // Compare JSON serialization assertNull( "cluster state JSON serialization does not match", diff --git a/test/framework/src/test/java/org/elasticsearch/logsdb/datageneration/SourceMatcherTests.java b/test/framework/src/test/java/org/elasticsearch/logsdb/datageneration/SourceMatcherTests.java new file mode 100644 index 0000000000000..74f70bae4d0c1 --- /dev/null +++ b/test/framework/src/test/java/org/elasticsearch/logsdb/datageneration/SourceMatcherTests.java @@ -0,0 +1,108 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +package org.elasticsearch.logsdb.datageneration; + +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.logsdb.datageneration.matchers.source.SourceMatcher; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.xcontent.XContentBuilder; +import org.elasticsearch.xcontent.XContentType; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +public class SourceMatcherTests extends ESTestCase { + public void testDynamicMatch() throws IOException { + List> values = List.of( + Map.of("aaa", 124, "bbb", false, "ccc", 12.34), + Map.of("aaa", 124, "bbb", false, "ccc", 12.34) + ); + + var sut = new SourceMatcher( + XContentBuilder.builder(XContentType.JSON.xContent()).startObject().endObject(), + Settings.builder(), + XContentBuilder.builder(XContentType.JSON.xContent()).startObject().endObject(), + Settings.builder(), + values, + values, + false + ); + assertTrue(sut.match().isMatch()); + } + + public void testDynamicMismatch() throws IOException { + List> actual = List.of( + Map.of("aaa", 124, "bbb", false, "ccc", 12.34), + Map.of("aaa", 124, "bbb", false, "ccc", 12.34) + ); + List> expected = List.of( + Map.of("aaa", 124, "bbb", false, "ccc", 12.34), + Map.of("aaa", 125, "bbb", false, "ccc", 12.34) + ); + + var sut = new SourceMatcher( + XContentBuilder.builder(XContentType.JSON.xContent()).startObject().endObject(), + Settings.builder(), + XContentBuilder.builder(XContentType.JSON.xContent()).startObject().endObject(), + Settings.builder(), + actual, + expected, + false + ); + assertFalse(sut.match().isMatch()); + } + + public void testMappedMatch() throws IOException { + List> values = List.of( + Map.of("aaa", 124, "bbb", false, "ccc", 12.34), + Map.of("aaa", 124, "bbb", false, "ccc", 12.34) + ); + + var mapping = XContentBuilder.builder(XContentType.JSON.xContent()); + mapping.startObject(); + mapping.startObject("_doc"); + { + mapping.startObject("aaa").field("type", "long").endObject(); + mapping.startObject("bbb").field("type", "boolean").endObject(); + mapping.startObject("ccc").field("type", "half_float").endObject(); + } + mapping.endObject(); + mapping.endObject(); + + var sut = new SourceMatcher(mapping, Settings.builder(), mapping, Settings.builder(), values, values, false); + assertTrue(sut.match().isMatch()); + } + + public void testMappedMismatch() throws IOException { + List> actual = List.of( + Map.of("aaa", 124, "bbb", false, "ccc", 12.34), + Map.of("aaa", 124, "bbb", false, "ccc", 12.34) + ); + List> expected = List.of( + Map.of("aaa", 124, "bbb", false, "ccc", 12.34), + Map.of("aaa", 124, "bbb", false, "ccc", 12.35) + ); + + var mapping = XContentBuilder.builder(XContentType.JSON.xContent()); + mapping.startObject(); + mapping.startObject("_doc"); + { + mapping.startObject("aaa").field("type", "long").endObject(); + mapping.startObject("bbb").field("type", "boolean").endObject(); + mapping.startObject("ccc").field("type", "half_float").endObject(); + } + mapping.endObject(); + mapping.endObject(); + + var sut = new SourceMatcher(mapping, Settings.builder(), mapping, Settings.builder(), actual, expected, false); + assertFalse(sut.match().isMatch()); + } +} diff --git a/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/FeatureFlag.java b/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/FeatureFlag.java index 896b245c8e920..5630c33ad559c 100644 --- a/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/FeatureFlag.java +++ b/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/FeatureFlag.java @@ -19,8 +19,7 @@ public enum FeatureFlag { TIME_SERIES_MODE("es.index_mode_feature_flag_registered=true", Version.fromString("8.0.0"), null), FAILURE_STORE_ENABLED("es.failure_store_feature_flag_enabled=true", Version.fromString("8.12.0"), null), SUB_OBJECTS_AUTO_ENABLED("es.sub_objects_auto_feature_flag_enabled=true", Version.fromString("8.16.0"), null), - INFERENCE_UNIFIED_API_ENABLED("es.inference_unified_feature_flag_enabled=true", Version.fromString("8.18.0"), null), - MIGRATION_REINDEX_ENABLED("es.reindex_data_stream_feature_flag_enabled=true", Version.fromString("8.18.0"), null); + INFERENCE_UNIFIED_API_ENABLED("es.inference_unified_feature_flag_enabled=true", Version.fromString("8.18.0"), null); public final String systemProperty; public final Version from; diff --git a/x-pack/plugin/build.gradle b/x-pack/plugin/build.gradle index 5ab0112d822ce..5987f75f4f198 100644 --- a/x-pack/plugin/build.gradle +++ b/x-pack/plugin/build.gradle @@ -96,5 +96,11 @@ tasks.named("yamlRestCompatTestTransform").configure({ task -> task.skipTest("esql/180_match_operator/match with non text field", "Match operator can now be used on non-text fields") task.skipTest("esql/180_match_operator/match with functions", "Error message changed") task.skipTest("esql/40_unsupported_types/semantic_text declared in mapping", "The semantic text field format changed") + task.skipTest("esql/190_lookup_join/Alias as lookup index", "LOOKUP JOIN does not support index aliases for now") + task.skipTest("esql/190_lookup_join/alias-repeated-alias", "LOOKUP JOIN does not support index aliases for now") + task.skipTest("esql/190_lookup_join/alias-repeated-index", "LOOKUP JOIN does not support index aliases for now") + task.skipTest("esql/190_lookup_join/alias-pattern-multiple", "LOOKUP JOIN does not support index aliases for now") + task.skipTest("esql/190_lookup_join/alias-pattern-single", "LOOKUP JOIN does not support index aliases for now") + }) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ClientHelper.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ClientHelper.java index 9a0d1a58a30a1..680b72cb970c9 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ClientHelper.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ClientHelper.java @@ -196,6 +196,9 @@ private static String maybeRewriteSingleAuthenticationHeaderForVersion( public static final String APM_ORIGIN = "apm"; public static final String OTEL_ORIGIN = "otel"; public static final String REINDEX_DATA_STREAM_ORIGIN = "reindex_data_stream"; + // TODO consolidate the Kibana origin with the one defined in org/elasticsearch/kibana/KibanaPlugin.java + public static final String KIBANA_ORIGIN = "kibana"; + public static final String CLOUD_ORIGIN = "cloud"; private ClientHelper() {} diff --git a/x-pack/plugin/core/template-resources/src/main/resources/entsearch/connector/elastic-connectors-sync-jobs-mappings.json b/x-pack/plugin/core/template-resources/src/main/resources/elastic-connectors-sync-jobs.json similarity index 88% rename from x-pack/plugin/core/template-resources/src/main/resources/entsearch/connector/elastic-connectors-sync-jobs-mappings.json rename to x-pack/plugin/core/template-resources/src/main/resources/elastic-connectors-sync-jobs.json index 4dd6e0681c7cc..7d1e7fa3a0418 100644 --- a/x-pack/plugin/core/template-resources/src/main/resources/entsearch/connector/elastic-connectors-sync-jobs-mappings.json +++ b/x-pack/plugin/core/template-resources/src/main/resources/elastic-connectors-sync-jobs.json @@ -1,12 +1,16 @@ { - "template": { - "aliases": { - ".elastic-connectors-sync-jobs": {} - }, - "mappings": { - "dynamic": "false", + "settings": { + "index": { + "number_of_shards": "1", + "auto_expand_replicas": "0-1" + } + }, + "mappings": { + "_doc": { + "dynamic": "strict", "_meta": { - "version": ${xpack.application.connector.template.version} + "version": "${elastic-connectors-sync-jobs.version}", + "managed_index_mappings_version": ${elastic-connectors-sync-jobs.managed.index.version} }, "properties": { "cancelation_requested_at": { @@ -21,9 +25,11 @@ "connector": { "properties": { "configuration": { + "dynamic": "false", "type": "object" }, "filtering": { + "dynamic": "false", "properties": { "advanced_snippet": { "properties": { @@ -91,6 +97,7 @@ "type": "keyword" }, "pipeline": { + "dynamic": "false", "properties": { "extract_binary_content": { "type": "boolean" @@ -110,6 +117,7 @@ "type": "keyword" }, "sync_cursor": { + "dynamic": "false", "type": "object" } } @@ -136,6 +144,7 @@ "type": "date" }, "metadata": { + "dynamic": "false", "type": "object" }, "started_at": { @@ -155,10 +164,5 @@ } } } - }, - "_meta": { - "description": "Built-in mappings applied by default to elastic-connectors indices", - "managed": true - }, - "version": ${xpack.application.connector.template.version} + } } diff --git a/x-pack/plugin/core/template-resources/src/main/resources/entsearch/connector/elastic-connectors-mappings.json b/x-pack/plugin/core/template-resources/src/main/resources/elastic-connectors.json similarity index 92% rename from x-pack/plugin/core/template-resources/src/main/resources/entsearch/connector/elastic-connectors-mappings.json rename to x-pack/plugin/core/template-resources/src/main/resources/elastic-connectors.json index 25409dbf8460e..a98018e76f0e0 100644 --- a/x-pack/plugin/core/template-resources/src/main/resources/entsearch/connector/elastic-connectors-mappings.json +++ b/x-pack/plugin/core/template-resources/src/main/resources/elastic-connectors.json @@ -1,29 +1,35 @@ { - "template": { - "aliases": { - ".elastic-connectors": {} - }, - "mappings": { - "dynamic": "false", + "settings": { + "index": { + "number_of_shards": "1", + "auto_expand_replicas": "0-1" + } + }, + "mappings": { + "_doc": { + "dynamic": "strict", "_meta": { - "pipeline": { - "default_name": "search-default-ingestion", - "default_extract_binary_content": true, - "default_run_ml_inference": true, - "default_reduce_whitespace": true - }, - "version": ${xpack.application.connector.template.version} + "version": "${elastic-connectors.version}", + "managed_index_mappings_version": ${elastic-connectors.managed.index.version} }, "properties": { "api_key_id": { "type": "keyword" }, + "api_key_secret_id": { + "type": "keyword" + }, "configuration": { + "dynamic": "false", "type": "object" }, "custom_scheduling": { + "dynamic": "false", "type": "object" }, + "deleted": { + "type": "boolean" + }, "description": { "type": "text" }, @@ -31,6 +37,7 @@ "type": "keyword" }, "features": { + "dynamic": "false", "properties": { "filtering_advanced_config": { "type": "boolean" @@ -66,6 +73,7 @@ } }, "filtering": { + "dynamic": "false", "properties": { "active": { "properties": { @@ -78,6 +86,7 @@ "type": "date" }, "value": { + "dynamic": "false", "type": "object" } } @@ -143,6 +152,7 @@ "type": "date" }, "value": { + "dynamic": "false", "type": "object" } } @@ -242,6 +252,7 @@ "type": "keyword" }, "pipeline": { + "dynamic": "false", "properties": { "extract_binary_content": { "type": "boolean" @@ -258,6 +269,7 @@ } }, "scheduling": { + "dynamic": "false", "properties": { "access_control": { "properties": { @@ -298,22 +310,13 @@ "type": "keyword" }, "sync_cursor": { + "dynamic": "false", "type": "object" }, "sync_now": { "type": "boolean" - }, - "deleted": { - "type": "boolean" } } } - }, - "_meta": { - "description": "Built-in mappings applied by default to elastic-connectors indices", - "managed": true - }, - "version": ${xpack.application.connector.template.version} + } } - - diff --git a/x-pack/plugin/core/template-resources/src/main/resources/entsearch/connector/elastic-connectors-settings.json b/x-pack/plugin/core/template-resources/src/main/resources/entsearch/connector/elastic-connectors-settings.json deleted file mode 100644 index 6ff9510574281..0000000000000 --- a/x-pack/plugin/core/template-resources/src/main/resources/entsearch/connector/elastic-connectors-settings.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "template": { - "settings": { - "hidden": true, - "number_of_shards": "1", - "auto_expand_replicas": "0-1" - } - }, - "_meta": { - "description": "Built-in settings applied by default to connector management indices", - "managed": true - }, - "version": ${xpack.application.connector.template.version} -} diff --git a/x-pack/plugin/core/template-resources/src/main/resources/entsearch/connector/elastic-connectors-sync-jobs.json b/x-pack/plugin/core/template-resources/src/main/resources/entsearch/connector/elastic-connectors-sync-jobs.json deleted file mode 100644 index db5404a30c6e4..0000000000000 --- a/x-pack/plugin/core/template-resources/src/main/resources/entsearch/connector/elastic-connectors-sync-jobs.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "index_patterns": ["${connectors-sync-jobs.index_pattern}"], - "priority": 100, - "composed_of": [ - "elastic-connectors-settings", - "elastic-connectors-sync-jobs-mappings" - ], - "allow_auto_create": true, - "_meta": { - "description": "Built-in template for elastic-connectors-sync-jobs", - "managed": true - }, - "version": ${xpack.application.connector.template.version} -} diff --git a/x-pack/plugin/core/template-resources/src/main/resources/entsearch/connector/elastic-connectors.json b/x-pack/plugin/core/template-resources/src/main/resources/entsearch/connector/elastic-connectors.json deleted file mode 100644 index 17c0b1eef0610..0000000000000 --- a/x-pack/plugin/core/template-resources/src/main/resources/entsearch/connector/elastic-connectors.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "index_patterns": ["${connectors.index_pattern}"], - "priority": 100, - "composed_of": [ - "elastic-connectors-settings", - "elastic-connectors-mappings" - ], - "allow_auto_create": true, - "_meta": { - "description": "Built-in template for elastic-connectors", - "managed": true - }, - "version": ${xpack.application.connector.template.version} -} diff --git a/x-pack/plugin/core/template-resources/src/main/resources/profiling/component-template/profiling-hosts.json b/x-pack/plugin/core/template-resources/src/main/resources/profiling/component-template/profiling-hosts.json index b0c99bf8a0cea..e23ae42ce1f4d 100644 --- a/x-pack/plugin/core/template-resources/src/main/resources/profiling/component-template/profiling-hosts.json +++ b/x-pack/plugin/core/template-resources/src/main/resources/profiling/component-template/profiling-hosts.json @@ -40,6 +40,9 @@ "id": { "type": "keyword" }, + "name": { + "type": "keyword" + }, "type": { "type": "keyword" } @@ -177,9 +180,6 @@ "tags": { "type": "keyword" }, - "name": { - "type": "keyword" - }, "machine": { "type": "keyword" }, diff --git a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java index df1c76ccf770f..4142d907d0c5c 100644 --- a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java +++ b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java @@ -46,6 +46,7 @@ import org.elasticsearch.xpack.application.analytics.action.TransportPutAnalyticsCollectionAction; import org.elasticsearch.xpack.application.analytics.ingest.AnalyticsEventIngestConfig; import org.elasticsearch.xpack.application.connector.ConnectorAPIFeature; +import org.elasticsearch.xpack.application.connector.ConnectorIndexService; import org.elasticsearch.xpack.application.connector.ConnectorTemplateRegistry; import org.elasticsearch.xpack.application.connector.action.DeleteConnectorAction; import org.elasticsearch.xpack.application.connector.action.GetConnectorAction; @@ -124,6 +125,7 @@ import org.elasticsearch.xpack.application.connector.secrets.action.TransportGetConnectorSecretAction; import org.elasticsearch.xpack.application.connector.secrets.action.TransportPostConnectorSecretAction; import org.elasticsearch.xpack.application.connector.secrets.action.TransportPutConnectorSecretAction; +import org.elasticsearch.xpack.application.connector.syncjob.ConnectorSyncJobIndexService; import org.elasticsearch.xpack.application.connector.syncjob.action.CancelConnectorSyncJobAction; import org.elasticsearch.xpack.application.connector.syncjob.action.CheckInConnectorSyncJobAction; import org.elasticsearch.xpack.application.connector.syncjob.action.ClaimConnectorSyncJobAction; @@ -477,7 +479,12 @@ public Collection createComponents(PluginServices services) { @Override public Collection getSystemIndexDescriptors(Settings settings) { Collection systemIndices = new ArrayList<>( - List.of(SearchApplicationIndexService.getSystemIndexDescriptor(), QueryRulesIndexService.getSystemIndexDescriptor()) + List.of( + SearchApplicationIndexService.getSystemIndexDescriptor(), + QueryRulesIndexService.getSystemIndexDescriptor(), + ConnectorSyncJobIndexService.getSystemIndexDescriptor(), + ConnectorIndexService.getSystemIndexDescriptor() + ) ); if (ConnectorSecretsFeature.isEnabled()) { diff --git a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/ConnectorIndexService.java b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/ConnectorIndexService.java index 3120124c17523..a9ca8552feeea 100644 --- a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/ConnectorIndexService.java +++ b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/ConnectorIndexService.java @@ -10,10 +10,12 @@ import org.elasticsearch.ElasticsearchStatusException; import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.ResourceNotFoundException; +import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.DelegatingActionListener; import org.elasticsearch.action.DocWriteRequest; import org.elasticsearch.action.DocWriteResponse; +import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest; import org.elasticsearch.action.delete.DeleteRequest; import org.elasticsearch.action.get.GetRequest; import org.elasticsearch.action.index.IndexRequest; @@ -33,6 +35,7 @@ import org.elasticsearch.index.query.TermQueryBuilder; import org.elasticsearch.index.query.TermsQueryBuilder; import org.elasticsearch.index.query.WildcardQueryBuilder; +import org.elasticsearch.indices.SystemIndexDescriptor; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptType; @@ -59,6 +62,7 @@ import org.elasticsearch.xpack.application.connector.filtering.FilteringValidationState; import org.elasticsearch.xpack.application.connector.syncjob.ConnectorSyncJob; import org.elasticsearch.xpack.application.connector.syncjob.ConnectorSyncJobIndexService; +import org.elasticsearch.xpack.core.template.TemplateUtils; import java.time.Instant; import java.util.ArrayList; @@ -76,6 +80,7 @@ import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.elasticsearch.xpack.application.connector.ConnectorFiltering.fromXContentBytesConnectorFiltering; import static org.elasticsearch.xpack.application.connector.ConnectorFiltering.sortFilteringRulesByOrder; +import static org.elasticsearch.xpack.application.connector.ConnectorTemplateRegistry.CONNECTORS_ALLOWED_PRODUCT_ORIGINS; import static org.elasticsearch.xpack.application.connector.ConnectorTemplateRegistry.MANAGED_CONNECTOR_INDEX_PREFIX; import static org.elasticsearch.xpack.core.ClientHelper.CONNECTORS_ORIGIN; @@ -87,7 +92,20 @@ public class ConnectorIndexService { // The client to interact with the system index (internal user). private final Client clientWithOrigin; - public static final String CONNECTOR_INDEX_NAME = ConnectorTemplateRegistry.CONNECTOR_INDEX_NAME_PATTERN; + // TODO use proper version IDs (see org/elasticsearch/xpack/application/rules/QueryRulesIndexService.java) + // TODO if this version is updated, a test should be added to + // javaRestTest/java/org/elasticsearch/xpack/application/FullClusterRestartIT.java + private static final int CONNECTORS_INDEX_VERSION = 1; + // TODO rename to CONNECTOR_ALIAS_NAME + public static final String CONNECTOR_INDEX_NAME = ".elastic-connectors"; + public static final String CONNECTOR_INDEX_PREFIX = ".elastic-connectors-v"; + public static final String CONNECTOR_CONCRETE_INDEX_NAME = CONNECTOR_INDEX_PREFIX + CONNECTORS_INDEX_VERSION; + // The index pattern needs a stricter regex to prevent conflicts with .elastic-connectors-sync-jobs + + public static final String CONNECTOR_INDEX_NAME_PATTERN = CONNECTOR_INDEX_PREFIX + "*"; + + private static final String CONNECTORS_MAPPING_VERSION_VARIABLE = "elastic-connectors.version"; + private static final String CONNECTORS_MAPPING_MANAGED_VERSION_VARIABLE = "elastic-connectors.managed.index.version"; /** * @param client A client for executing actions on the connector index @@ -96,6 +114,36 @@ public ConnectorIndexService(Client client) { this.clientWithOrigin = new OriginSettingClient(client, CONNECTORS_ORIGIN); } + /** + * Returns the {@link SystemIndexDescriptor} for the Connector system index. + * + * @return The {@link SystemIndexDescriptor} for the Connector system index. + */ + public static SystemIndexDescriptor getSystemIndexDescriptor() { + PutIndexTemplateRequest request = new PutIndexTemplateRequest(); + String templateSource = TemplateUtils.loadTemplate( + "/elastic-connectors.json", + Version.CURRENT.toString(), + CONNECTORS_MAPPING_VERSION_VARIABLE, + Map.of(CONNECTORS_MAPPING_MANAGED_VERSION_VARIABLE, Integer.toString(CONNECTORS_INDEX_VERSION)) + ); + request.source(templateSource, XContentType.JSON); + + // The index pattern needs a stricter regex to prevent conflicts with .elastic-connectors-sync-jobs + return SystemIndexDescriptor.builder() + .setIndexPattern(CONNECTOR_INDEX_NAME_PATTERN) + .setPrimaryIndex(CONNECTOR_CONCRETE_INDEX_NAME) + .setAliasName(CONNECTOR_INDEX_NAME) + .setDescription("Search connectors") + .setMappings(request.mappings()) + .setSettings(request.settings()) + .setOrigin(CONNECTORS_ORIGIN) + .setType(SystemIndexDescriptor.Type.EXTERNAL_MANAGED) + .setAllowedElasticProductOrigins(CONNECTORS_ALLOWED_PRODUCT_ORIGINS) + .setNetNew() + .build(); + } + /** * Creates or updates the {@link Connector} in the underlying index with a specific doc ID * if connectorId is provided. Otherwise, the connector doc is indexed with auto-generated doc ID. diff --git a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/ConnectorTemplateRegistry.java b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/ConnectorTemplateRegistry.java index fd35acc89db5c..97ac05c443ad0 100644 --- a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/ConnectorTemplateRegistry.java +++ b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/ConnectorTemplateRegistry.java @@ -8,25 +8,23 @@ package org.elasticsearch.xpack.application.connector; import org.elasticsearch.client.internal.Client; -import org.elasticsearch.cluster.metadata.ComponentTemplate; import org.elasticsearch.cluster.metadata.ComposableIndexTemplate; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.xcontent.NamedXContentRegistry; -import org.elasticsearch.xcontent.XContentParserConfiguration; -import org.elasticsearch.xcontent.json.JsonXContent; import org.elasticsearch.xpack.core.template.IndexTemplateConfig; import org.elasticsearch.xpack.core.template.IndexTemplateRegistry; import org.elasticsearch.xpack.core.template.IngestPipelineConfig; import org.elasticsearch.xpack.core.template.JsonIngestPipelineConfig; -import java.io.IOException; -import java.util.HashMap; import java.util.List; import java.util.Map; +import static org.elasticsearch.xpack.core.ClientHelper.CLOUD_ORIGIN; +import static org.elasticsearch.xpack.core.ClientHelper.CONNECTORS_ORIGIN; import static org.elasticsearch.xpack.core.ClientHelper.ENT_SEARCH_ORIGIN; +import static org.elasticsearch.xpack.core.ClientHelper.KIBANA_ORIGIN; public class ConnectorTemplateRegistry extends IndexTemplateRegistry { @@ -34,13 +32,6 @@ public class ConnectorTemplateRegistry extends IndexTemplateRegistry { static final int REGISTRY_VERSION = 3; // Connector indices constants - - public static final String CONNECTOR_INDEX_NAME_PATTERN = ".elastic-connectors-v1"; - public static final String CONNECTOR_TEMPLATE_NAME = "elastic-connectors"; - - public static final String CONNECTOR_SYNC_JOBS_INDEX_NAME_PATTERN = ".elastic-connectors-sync-jobs-v1"; - public static final String CONNECTOR_SYNC_JOBS_TEMPLATE_NAME = "elastic-connectors-sync-jobs"; - public static final String ACCESS_CONTROL_INDEX_PREFIX = ".search-acl-filter-"; public static final String ACCESS_CONTROL_INDEX_NAME_PATTERN = ".search-acl-filter-*"; public static final String ACCESS_CONTROL_TEMPLATE_NAME = "search-acl-filter"; @@ -58,51 +49,8 @@ public class ConnectorTemplateRegistry extends IndexTemplateRegistry { // Variable used to replace template version in index templates public static final String TEMPLATE_VERSION_VARIABLE = "xpack.application.connector.template.version"; - private static final String MAPPINGS_SUFFIX = "-mappings"; - - private static final String SETTINGS_SUFFIX = "-settings"; - - private static final String JSON_EXTENSION = ".json"; - - static final Map COMPONENT_TEMPLATES; - - static { - final Map componentTemplates = new HashMap<>(); - for (IndexTemplateConfig config : List.of( - new IndexTemplateConfig( - CONNECTOR_TEMPLATE_NAME + MAPPINGS_SUFFIX, - ROOT_TEMPLATE_RESOURCE_PATH + CONNECTOR_TEMPLATE_NAME + MAPPINGS_SUFFIX + JSON_EXTENSION, - REGISTRY_VERSION, - TEMPLATE_VERSION_VARIABLE - ), - new IndexTemplateConfig( - CONNECTOR_TEMPLATE_NAME + SETTINGS_SUFFIX, - ROOT_TEMPLATE_RESOURCE_PATH + CONNECTOR_TEMPLATE_NAME + SETTINGS_SUFFIX + JSON_EXTENSION, - REGISTRY_VERSION, - TEMPLATE_VERSION_VARIABLE - ), - new IndexTemplateConfig( - CONNECTOR_SYNC_JOBS_TEMPLATE_NAME + MAPPINGS_SUFFIX, - ROOT_TEMPLATE_RESOURCE_PATH + CONNECTOR_SYNC_JOBS_TEMPLATE_NAME + MAPPINGS_SUFFIX + JSON_EXTENSION, - REGISTRY_VERSION, - TEMPLATE_VERSION_VARIABLE - ), - new IndexTemplateConfig( - CONNECTOR_SYNC_JOBS_TEMPLATE_NAME + SETTINGS_SUFFIX, - ROOT_TEMPLATE_RESOURCE_PATH + CONNECTOR_TEMPLATE_NAME + SETTINGS_SUFFIX + JSON_EXTENSION, - REGISTRY_VERSION, - TEMPLATE_VERSION_VARIABLE - ) - )) { - - try (var parser = JsonXContent.jsonXContent.createParser(XContentParserConfiguration.EMPTY, config.loadBytes())) { - componentTemplates.put(config.getTemplateName(), ComponentTemplate.parse(parser)); - } catch (IOException e) { - throw new AssertionError(e); - } - } - COMPONENT_TEMPLATES = Map.copyOf(componentTemplates); - } + // Sources allowed to access system indices using X-elastic-product-origin header + public static final List CONNECTORS_ALLOWED_PRODUCT_ORIGINS = List.of(KIBANA_ORIGIN, CONNECTORS_ORIGIN, CLOUD_ORIGIN); @Override protected List getIngestPipelines() { @@ -117,20 +65,6 @@ protected List getIngestPipelines() { } static final Map COMPOSABLE_INDEX_TEMPLATES = parseComposableTemplates( - new IndexTemplateConfig( - CONNECTOR_TEMPLATE_NAME, - ROOT_TEMPLATE_RESOURCE_PATH + CONNECTOR_TEMPLATE_NAME + ".json", - REGISTRY_VERSION, - TEMPLATE_VERSION_VARIABLE, - Map.of("connectors.index_pattern", CONNECTOR_INDEX_NAME_PATTERN) - ), - new IndexTemplateConfig( - CONNECTOR_SYNC_JOBS_TEMPLATE_NAME, - ROOT_TEMPLATE_RESOURCE_PATH + CONNECTOR_SYNC_JOBS_TEMPLATE_NAME + ".json", - REGISTRY_VERSION, - TEMPLATE_VERSION_VARIABLE, - Map.of("connectors-sync-jobs.index_pattern", CONNECTOR_SYNC_JOBS_INDEX_NAME_PATTERN) - ), new IndexTemplateConfig( ACCESS_CONTROL_TEMPLATE_NAME, ROOT_TEMPLATE_RESOURCE_PATH + ACCESS_CONTROL_TEMPLATE_NAME + ".json", @@ -154,11 +88,6 @@ protected String getOrigin() { return ENT_SEARCH_ORIGIN; } - @Override - protected Map getComponentTemplateConfigs() { - return COMPONENT_TEMPLATES; - } - @Override protected Map getComposableTemplateConfigs() { return COMPOSABLE_INDEX_TEMPLATES; diff --git a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/syncjob/ConnectorSyncJobIndexService.java b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/syncjob/ConnectorSyncJobIndexService.java index f46d915a7123f..85de2f900ddff 100644 --- a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/syncjob/ConnectorSyncJobIndexService.java +++ b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/syncjob/ConnectorSyncJobIndexService.java @@ -11,10 +11,12 @@ import org.elasticsearch.ElasticsearchStatusException; import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.ResourceNotFoundException; +import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.DelegatingActionListener; import org.elasticsearch.action.DocWriteRequest; import org.elasticsearch.action.DocWriteResponse; +import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest; import org.elasticsearch.action.bulk.BulkItemResponse; import org.elasticsearch.action.delete.DeleteRequest; import org.elasticsearch.action.delete.DeleteResponse; @@ -40,6 +42,7 @@ import org.elasticsearch.index.reindex.BulkByScrollResponse; import org.elasticsearch.index.reindex.DeleteByQueryAction; import org.elasticsearch.index.reindex.DeleteByQueryRequest; +import org.elasticsearch.indices.SystemIndexDescriptor; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.builder.SearchSourceBuilder; @@ -49,10 +52,10 @@ import org.elasticsearch.xpack.application.connector.Connector; import org.elasticsearch.xpack.application.connector.ConnectorFiltering; import org.elasticsearch.xpack.application.connector.ConnectorSyncStatus; -import org.elasticsearch.xpack.application.connector.ConnectorTemplateRegistry; import org.elasticsearch.xpack.application.connector.filtering.FilteringRules; import org.elasticsearch.xpack.application.connector.syncjob.action.PostConnectorSyncJobAction; import org.elasticsearch.xpack.application.connector.syncjob.action.UpdateConnectorSyncJobIngestionStatsAction; +import org.elasticsearch.xpack.core.template.TemplateUtils; import java.io.IOException; import java.time.Instant; @@ -69,6 +72,7 @@ import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.elasticsearch.xpack.application.connector.ConnectorIndexService.CONNECTOR_INDEX_NAME; +import static org.elasticsearch.xpack.application.connector.ConnectorTemplateRegistry.CONNECTORS_ALLOWED_PRODUCT_ORIGINS; import static org.elasticsearch.xpack.core.ClientHelper.CONNECTORS_ORIGIN; /** @@ -81,7 +85,17 @@ public class ConnectorSyncJobIndexService { // The client to interact with the system index (internal user). private final Client clientWithOrigin; - public static final String CONNECTOR_SYNC_JOB_INDEX_NAME = ConnectorTemplateRegistry.CONNECTOR_SYNC_JOBS_INDEX_NAME_PATTERN; + // TODO use proper version IDs (see org/elasticsearch/xpack/application/rules/QueryRulesIndexService.java) + // TODO if this version is updated, a test should be added to + // javaRestTest/java/org/elasticsearch/xpack/application/FullClusterRestartIT.java + private static final int CONNECTOR_SYNC_JOB_INDEX_VERSION = 1; + public static final String CONNECTOR_SYNC_JOB_INDEX_NAME = ".elastic-connectors-sync-jobs"; + public static final String CONNECTOR_SYNC_JOB_INDEX_PREFIX = ".elastic-connectors-sync-jobs-v"; + public static final String CONNECTOR_SYNC_JOB_CONCRETE_INDEX_NAME = CONNECTOR_SYNC_JOB_INDEX_PREFIX + CONNECTOR_SYNC_JOB_INDEX_VERSION; + public static final String CONNECTOR_SYNC_JOB_INDEX_NAME_PATTERN = CONNECTOR_SYNC_JOB_INDEX_NAME + "*"; + + private static final String CONNECTOR_SYNC_JOB_MAPPING_VERSION_VARIABLE = "elastic-connectors-sync-jobs.version"; + private static final String CONNECTOR_SYNC_JOB_MAPPING_MANAGED_VERSION_VARIABLE = "elastic-connectors-sync-jobs.managed.index.version"; /** * @param client A client for executing actions on the connectors sync jobs index. @@ -90,6 +104,35 @@ public ConnectorSyncJobIndexService(Client client) { this.clientWithOrigin = new OriginSettingClient(client, CONNECTORS_ORIGIN); } + /** + * Returns the {@link SystemIndexDescriptor} for the Connector system index. + * + * @return The {@link SystemIndexDescriptor} for the Connector system index. + */ + public static SystemIndexDescriptor getSystemIndexDescriptor() { + PutIndexTemplateRequest request = new PutIndexTemplateRequest(); + String templateSource = TemplateUtils.loadTemplate( + "/elastic-connectors-sync-jobs.json", + Version.CURRENT.toString(), + CONNECTOR_SYNC_JOB_MAPPING_VERSION_VARIABLE, + Map.of(CONNECTOR_SYNC_JOB_MAPPING_MANAGED_VERSION_VARIABLE, Integer.toString(CONNECTOR_SYNC_JOB_INDEX_VERSION)) + ); + request.source(templateSource, XContentType.JSON); + + return SystemIndexDescriptor.builder() + .setIndexPattern(CONNECTOR_SYNC_JOB_INDEX_NAME_PATTERN) + .setPrimaryIndex(CONNECTOR_SYNC_JOB_CONCRETE_INDEX_NAME) + .setAliasName(CONNECTOR_SYNC_JOB_INDEX_NAME) + .setDescription("Search connectors sync jobs") + .setMappings(request.mappings()) + .setSettings(request.settings()) + .setOrigin(CONNECTORS_ORIGIN) + .setType(SystemIndexDescriptor.Type.EXTERNAL_MANAGED) + .setAllowedElasticProductOrigins(CONNECTORS_ALLOWED_PRODUCT_ORIGINS) + .setNetNew() + .build(); + } + /** * @param request Request for creating a connector sync job. * @param listener Listener to respond to a successful response or an error. diff --git a/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/connector/ConnectorIndexServiceTests.java b/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/connector/ConnectorIndexServiceTests.java index 7b6d9c9b14df9..53a8c7ac96944 100644 --- a/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/connector/ConnectorIndexServiceTests.java +++ b/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/connector/ConnectorIndexServiceTests.java @@ -14,7 +14,9 @@ import org.elasticsearch.action.update.UpdateResponse; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.core.Tuple; +import org.elasticsearch.indices.SystemIndexDescriptor; import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.plugins.SystemIndexPlugin; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.script.MockScriptEngine; import org.elasticsearch.script.MockScriptPlugin; @@ -59,7 +61,6 @@ import static org.elasticsearch.xpack.application.connector.ConnectorTestUtils.getRandomConnectorFeatures; import static org.elasticsearch.xpack.application.connector.ConnectorTestUtils.getRandomCronExpression; import static org.elasticsearch.xpack.application.connector.ConnectorTestUtils.randomConnectorFeatureEnabled; -import static org.elasticsearch.xpack.application.connector.ConnectorTestUtils.registerSimplifiedConnectorIndexTemplates; import static org.hamcrest.CoreMatchers.anyOf; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.not; @@ -72,7 +73,6 @@ public class ConnectorIndexServiceTests extends ESSingleNodeTestCase { @Before public void setup() { - registerSimplifiedConnectorIndexTemplates(indicesAdmin()); this.connectorIndexService = new ConnectorIndexService(client()); } @@ -80,6 +80,7 @@ public void setup() { protected Collection> getPlugins() { List> plugins = new ArrayList<>(super.getPlugins()); plugins.add(MockPainlessScriptEngine.TestPlugin.class); + plugins.add(ConnectorIndexServiceTests.TestPlugin.class); return plugins; } @@ -1612,4 +1613,24 @@ public void execute() { } } + /** + * Test plugin to register the {@link ConnectorIndexService} system index descriptor. + */ + public static class TestPlugin extends Plugin implements SystemIndexPlugin { + @Override + public Collection getSystemIndexDescriptors(Settings settings) { + return List.of(ConnectorIndexService.getSystemIndexDescriptor()); + } + + @Override + public String getFeatureName() { + return this.getClass().getSimpleName(); + } + + @Override + public String getFeatureDescription() { + return this.getClass().getCanonicalName(); + } + } + } diff --git a/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/connector/ConnectorTemplateRegistryTests.java b/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/connector/ConnectorTemplateRegistryTests.java index 068b99626af9d..89bdabe78300c 100644 --- a/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/connector/ConnectorTemplateRegistryTests.java +++ b/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/connector/ConnectorTemplateRegistryTests.java @@ -55,15 +55,13 @@ import java.util.stream.Collectors; import static org.elasticsearch.xpack.application.connector.ConnectorTemplateRegistry.ACCESS_CONTROL_INDEX_NAME_PATTERN; -import static org.elasticsearch.xpack.application.connector.ConnectorTemplateRegistry.CONNECTOR_INDEX_NAME_PATTERN; -import static org.elasticsearch.xpack.application.connector.ConnectorTemplateRegistry.CONNECTOR_SYNC_JOBS_INDEX_NAME_PATTERN; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.oneOf; import static org.hamcrest.Matchers.sameInstance; +import static org.junit.Assert.assertNotNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @@ -92,14 +90,6 @@ public void testThatNonExistingComposableTemplatesAreAddedImmediately() throws E DiscoveryNode node = DiscoveryNodeUtils.create("node"); DiscoveryNodes nodes = DiscoveryNodes.builder().localNodeId("node").masterNodeId("node").add(node).build(); Map existingComponentTemplates = Map.of( - ConnectorTemplateRegistry.CONNECTOR_TEMPLATE_NAME + "-mappings", - ConnectorTemplateRegistry.REGISTRY_VERSION, - ConnectorTemplateRegistry.CONNECTOR_TEMPLATE_NAME + "-settings", - ConnectorTemplateRegistry.REGISTRY_VERSION, - ConnectorTemplateRegistry.CONNECTOR_SYNC_JOBS_TEMPLATE_NAME + "-mappings", - ConnectorTemplateRegistry.REGISTRY_VERSION, - ConnectorTemplateRegistry.CONNECTOR_SYNC_JOBS_TEMPLATE_NAME + "-settings", - ConnectorTemplateRegistry.REGISTRY_VERSION, ConnectorTemplateRegistry.ACCESS_CONTROL_TEMPLATE_NAME, ConnectorTemplateRegistry.REGISTRY_VERSION ); @@ -125,131 +115,6 @@ public void testThatNonExistingComposableTemplatesAreAddedImmediately() throws E }); } - public void testThatNonExistingComponentTemplatesAreAddedImmediately() throws Exception { - DiscoveryNode node = DiscoveryNodeUtils.create("node"); - DiscoveryNodes nodes = DiscoveryNodes.builder().localNodeId("node").masterNodeId("node").add(node).build(); - - ClusterChangedEvent event = createClusterChangedEvent( - Collections.emptyMap(), - Collections.emptyMap(), - Collections.singletonMap(ConnectorTemplateRegistry.SEARCH_DEFAULT_PIPELINE_NAME, ConnectorTemplateRegistry.REGISTRY_VERSION), - Collections.emptyMap(), - nodes - ); - - AtomicInteger calledTimes = new AtomicInteger(0); - client.setVerifier((action, request, listener) -> verifyComponentTemplateInstalled(calledTimes, action, request, listener)); - registry.clusterChanged(event); - assertBusy(() -> assertThat(calledTimes.get(), equalTo(registry.getComponentTemplateConfigs().size()))); - - calledTimes.set(0); - - // attempting to register the event multiple times as a race condition can yield this test flaky, namely: - // when calling registry.clusterChanged(newEvent) the templateCreationsInProgress state that the IndexTemplateRegistry maintains - // might've not yet been updated to reflect that the first template registration was complete, so a second template registration - // will not be issued anymore, leaving calledTimes to 0 - assertBusy(() -> { - // now delete all templates from the cluster state and let's retry - ClusterChangedEvent newEvent = createClusterChangedEvent(Collections.emptyMap(), Collections.emptyMap(), nodes); - registry.clusterChanged(newEvent); - assertThat(calledTimes.get(), greaterThan(4)); - }); - } - - public void testThatVersionedOldComponentTemplatesAreUpgraded() throws Exception { - DiscoveryNode node = DiscoveryNodeUtils.create("node"); - DiscoveryNodes nodes = DiscoveryNodes.builder().localNodeId("node").masterNodeId("node").add(node).build(); - - ClusterChangedEvent event = createClusterChangedEvent( - Collections.emptyMap(), - Collections.singletonMap( - ConnectorTemplateRegistry.CONNECTOR_TEMPLATE_NAME + "-settings", - ConnectorTemplateRegistry.REGISTRY_VERSION - 1 - ), - Collections.singletonMap(ConnectorTemplateRegistry.SEARCH_DEFAULT_PIPELINE_NAME, ConnectorTemplateRegistry.REGISTRY_VERSION), - Collections.emptyMap(), - nodes - ); - AtomicInteger calledTimes = new AtomicInteger(0); - client.setVerifier((action, request, listener) -> verifyComponentTemplateInstalled(calledTimes, action, request, listener)); - registry.clusterChanged(event); - assertBusy(() -> assertThat(calledTimes.get(), equalTo(registry.getComponentTemplateConfigs().size()))); - } - - public void testThatUnversionedOldComponentTemplatesAreUpgraded() throws Exception { - DiscoveryNode node = DiscoveryNodeUtils.create("node"); - DiscoveryNodes nodes = DiscoveryNodes.builder().localNodeId("node").masterNodeId("node").add(node).build(); - - ClusterChangedEvent event = createClusterChangedEvent( - Collections.emptyMap(), - Collections.singletonMap(ConnectorTemplateRegistry.CONNECTOR_TEMPLATE_NAME + "-mappings", null), - Collections.singletonMap(ConnectorTemplateRegistry.SEARCH_DEFAULT_PIPELINE_NAME, ConnectorTemplateRegistry.REGISTRY_VERSION), - Collections.emptyMap(), - nodes - ); - AtomicInteger calledTimes = new AtomicInteger(0); - client.setVerifier((action, request, listener) -> verifyComponentTemplateInstalled(calledTimes, action, request, listener)); - registry.clusterChanged(event); - assertBusy(() -> assertThat(calledTimes.get(), equalTo(registry.getComponentTemplateConfigs().size()))); - } - - public void testSameOrHigherVersionComponentTemplateNotUpgraded() { - DiscoveryNode node = DiscoveryNodeUtils.create("node"); - DiscoveryNodes nodes = DiscoveryNodes.builder().localNodeId("node").masterNodeId("node").add(node).build(); - - Map versions = new HashMap<>(); - versions.put(ConnectorTemplateRegistry.CONNECTOR_TEMPLATE_NAME + "-mappings", ConnectorTemplateRegistry.REGISTRY_VERSION); - versions.put(ConnectorTemplateRegistry.CONNECTOR_TEMPLATE_NAME + "-settings", ConnectorTemplateRegistry.REGISTRY_VERSION); - versions.put(ConnectorTemplateRegistry.CONNECTOR_SYNC_JOBS_TEMPLATE_NAME + "-mappings", ConnectorTemplateRegistry.REGISTRY_VERSION); - versions.put(ConnectorTemplateRegistry.CONNECTOR_SYNC_JOBS_TEMPLATE_NAME + "-settings", ConnectorTemplateRegistry.REGISTRY_VERSION); - versions.put(ConnectorTemplateRegistry.ACCESS_CONTROL_TEMPLATE_NAME, ConnectorTemplateRegistry.REGISTRY_VERSION); - ClusterChangedEvent sameVersionEvent = createClusterChangedEvent(Collections.emptyMap(), versions, nodes); - client.setVerifier((action, request, listener) -> { - if (action == PutPipelineTransportAction.TYPE) { - // Ignore this, it's verified in another test - return AcknowledgedResponse.TRUE; - } - if (action instanceof PutComponentTemplateAction) { - fail("template should not have been re-installed"); - return null; - } else if (action == ILMActions.PUT) { - // Ignore this, it's verified in another test - return AcknowledgedResponse.TRUE; - } else if (action == TransportPutComposableIndexTemplateAction.TYPE) { - // Ignore this, it's verified in another test - return AcknowledgedResponse.TRUE; - } else { - fail("client called with unexpected request:" + request.toString()); - return null; - } - }); - registry.clusterChanged(sameVersionEvent); - - versions.clear(); - versions.put( - ConnectorTemplateRegistry.CONNECTOR_TEMPLATE_NAME + "-mappings", - ConnectorTemplateRegistry.REGISTRY_VERSION + randomIntBetween(0, 1000) - ); - versions.put( - ConnectorTemplateRegistry.CONNECTOR_TEMPLATE_NAME + "-settings", - ConnectorTemplateRegistry.REGISTRY_VERSION + randomIntBetween(0, 1000) - ); - versions.put( - ConnectorTemplateRegistry.CONNECTOR_SYNC_JOBS_TEMPLATE_NAME + "-mappings", - ConnectorTemplateRegistry.REGISTRY_VERSION + randomIntBetween(0, 1000) - ); - versions.put( - ConnectorTemplateRegistry.CONNECTOR_SYNC_JOBS_TEMPLATE_NAME + "-settings", - ConnectorTemplateRegistry.REGISTRY_VERSION + randomIntBetween(0, 1000) - ); - versions.put( - ConnectorTemplateRegistry.ACCESS_CONTROL_TEMPLATE_NAME, - ConnectorTemplateRegistry.REGISTRY_VERSION + randomIntBetween(0, 1000) - ); - ClusterChangedEvent higherVersionEvent = createClusterChangedEvent(Collections.emptyMap(), versions, nodes); - registry.clusterChanged(higherVersionEvent); - } - public void testThatMissingMasterNodeDoesNothing() { DiscoveryNode localNode = DiscoveryNodeUtils.create("node"); DiscoveryNodes nodes = DiscoveryNodes.builder().localNodeId("node").add(localNode).build(); @@ -260,7 +125,7 @@ public void testThatMissingMasterNodeDoesNothing() { }); ClusterChangedEvent event = createClusterChangedEvent( - Collections.singletonMap(ConnectorTemplateRegistry.CONNECTOR_TEMPLATE_NAME, null), + Collections.singletonMap(ConnectorTemplateRegistry.ACCESS_CONTROL_TEMPLATE_NAME, null), Collections.emptyMap(), nodes ); @@ -357,10 +222,7 @@ private ActionResponse verifyComposableTemplateInstalled( assertThat(putRequest.indexTemplate().version(), equalTo((long) ConnectorTemplateRegistry.REGISTRY_VERSION)); final List indexPatterns = putRequest.indexTemplate().indexPatterns(); assertThat(indexPatterns, hasSize(1)); - assertThat( - indexPatterns, - contains(oneOf(ACCESS_CONTROL_INDEX_NAME_PATTERN, CONNECTOR_INDEX_NAME_PATTERN, CONNECTOR_SYNC_JOBS_INDEX_NAME_PATTERN)) - ); + assertThat(indexPatterns, contains(ACCESS_CONTROL_INDEX_NAME_PATTERN)); assertNotNull(listener); return new TestPutIndexTemplateResponse(true); } else { diff --git a/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/connector/ConnectorTestUtils.java b/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/connector/ConnectorTestUtils.java index c563bc0a14ee3..3f2f47e190882 100644 --- a/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/connector/ConnectorTestUtils.java +++ b/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/connector/ConnectorTestUtils.java @@ -7,7 +7,6 @@ package org.elasticsearch.xpack.application.connector; -import org.elasticsearch.client.internal.IndicesAdminClient; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.xcontent.XContentType; @@ -27,7 +26,6 @@ import org.elasticsearch.xpack.application.connector.filtering.FilteringValidation; import org.elasticsearch.xpack.application.connector.filtering.FilteringValidationInfo; import org.elasticsearch.xpack.application.connector.filtering.FilteringValidationState; -import org.elasticsearch.xpack.application.connector.syncjob.ConnectorSyncJob; import org.elasticsearch.xpack.application.connector.syncjob.ConnectorSyncJobType; import org.elasticsearch.xpack.core.scheduler.Cron; @@ -47,55 +45,14 @@ import static org.elasticsearch.test.ESTestCase.randomFrom; import static org.elasticsearch.test.ESTestCase.randomInt; import static org.elasticsearch.test.ESTestCase.randomList; -import static org.elasticsearch.test.ESTestCase.randomLong; import static org.elasticsearch.test.ESTestCase.randomLongBetween; -import static org.elasticsearch.xpack.application.connector.ConnectorTemplateRegistry.CONNECTOR_INDEX_NAME_PATTERN; -import static org.elasticsearch.xpack.application.connector.ConnectorTemplateRegistry.CONNECTOR_SYNC_JOBS_INDEX_NAME_PATTERN; -import static org.elasticsearch.xpack.application.connector.ConnectorTemplateRegistry.CONNECTOR_SYNC_JOBS_TEMPLATE_NAME; -import static org.elasticsearch.xpack.application.connector.ConnectorTemplateRegistry.CONNECTOR_TEMPLATE_NAME; +import static org.elasticsearch.test.ESTestCase.randomNonNegativeLong; +import static org.elasticsearch.test.ESTestCase.randomShort; public final class ConnectorTestUtils { public static final String NULL_STRING = null; - /** - * Registers index templates for instances of {@link Connector} and {@link ConnectorSyncJob} with essential field mappings. This method - * only includes mappings for fields relevant to test cases, specifying field types to ensure correct ES query logic behavior. - * - * @param indicesAdminClient The Elasticsearch indices admin client used for template registration. - */ - - public static void registerSimplifiedConnectorIndexTemplates(IndicesAdminClient indicesAdminClient) { - - indicesAdminClient.preparePutTemplate(CONNECTOR_TEMPLATE_NAME) - .setPatterns(List.of(CONNECTOR_INDEX_NAME_PATTERN)) - .setVersion(0) - .setMapping( - "service_type", - "type=keyword,store=true", - "status", - "type=keyword,store=true", - "index_name", - "type=keyword,store=true", - "configuration", - "type=object" - ) - .get(); - - indicesAdminClient.preparePutTemplate(CONNECTOR_SYNC_JOBS_TEMPLATE_NAME) - .setPatterns(List.of(CONNECTOR_SYNC_JOBS_INDEX_NAME_PATTERN)) - .setVersion(0) - .setMapping( - "job_type", - "type=keyword,store=true", - "connector.id", - "type=keyword,store=true", - "status", - "type=keyword,store=true" - ) - .get(); - } - public static PutConnectorAction.Request getRandomPutConnectorActionRequest() { return new PutConnectorAction.Request( randomAlphaOfLengthBetween(5, 15), @@ -144,9 +101,9 @@ public static ConnectorSyncInfo getRandomConnectorSyncInfo() { return new ConnectorSyncInfo.Builder().setLastAccessControlSyncError(randomFrom(new String[] { null, randomAlphaOfLength(10) })) .setLastAccessControlSyncScheduledAt(randomFrom(new Instant[] { null, ConnectorTestUtils.randomInstant() })) .setLastAccessControlSyncStatus(randomFrom(new ConnectorSyncStatus[] { null, getRandomSyncStatus() })) - .setLastDeletedDocumentCount(randomLong()) + .setLastDeletedDocumentCount(randomNonNegativeLong()) .setLastIncrementalSyncScheduledAt(randomFrom(new Instant[] { null, ConnectorTestUtils.randomInstant() })) - .setLastIndexedDocumentCount(randomLong()) + .setLastIndexedDocumentCount(randomNonNegativeLong()) .setLastSyncError(randomFrom(new String[] { null, randomAlphaOfLength(10) })) .setLastSyncScheduledAt(randomFrom(new Instant[] { null, ConnectorTestUtils.randomInstant() })) .setLastSyncStatus(randomFrom(new ConnectorSyncStatus[] { null, getRandomSyncStatus() })) @@ -197,7 +154,7 @@ private static FilteringValidation getRandomFilteringValidationError() { public static ConnectorFiltering getRandomConnectorFiltering() { Instant currentTimestamp = Instant.now(); - int order = randomInt(); + int order = randomShort(); return new ConnectorFiltering.Builder().setActive( new FilteringRules.Builder().setAdvancedSnippet( diff --git a/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/connector/syncjob/ConnectorSyncJobIndexServiceTests.java b/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/connector/syncjob/ConnectorSyncJobIndexServiceTests.java index f6c0a54f107b4..fe6d97a871e0c 100644 --- a/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/connector/syncjob/ConnectorSyncJobIndexServiceTests.java +++ b/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/connector/syncjob/ConnectorSyncJobIndexServiceTests.java @@ -20,8 +20,11 @@ import org.elasticsearch.action.support.WriteRequest; import org.elasticsearch.action.update.UpdateRequest; import org.elasticsearch.action.update.UpdateResponse; +import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.reindex.BulkByScrollResponse; +import org.elasticsearch.indices.SystemIndexDescriptor; import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.plugins.SystemIndexPlugin; import org.elasticsearch.reindex.ReindexPlugin; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.test.ESSingleNodeTestCase; @@ -58,7 +61,6 @@ import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.elasticsearch.xpack.application.connector.ConnectorTemplateRegistry.ACCESS_CONTROL_INDEX_PREFIX; -import static org.elasticsearch.xpack.application.connector.ConnectorTestUtils.registerSimplifiedConnectorIndexTemplates; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThanOrEqualTo; @@ -86,14 +88,12 @@ protected Collection> getPlugins() { List> plugins = new ArrayList<>(super.getPlugins()); // Reindex plugin is required for testDeleteAllSyncJobsByConnectorId (supports delete_by_query) plugins.add(ReindexPlugin.class); + plugins.add(TestPlugin.class); return plugins; } @Before public void setup() throws Exception { - - registerSimplifiedConnectorIndexTemplates(indicesAdmin()); - connectorOneId = createConnector(ConnectorTestUtils.getRandomConnector()); connectorTwoId = createConnector(ConnectorTestUtils.getRandomConnector()); connectorThreeId = createConnector(ConnectorTestUtils.getRandomConnectorWithDetachedIndex()); @@ -805,18 +805,18 @@ public void testUpdateConnectorSyncJobIngestionStats() throws Exception { Instant requestLastSeen = request.getLastSeen(); Map metadata = request.getMetadata(); - Long deletedDocumentCountAfterUpdate = (Long) syncJobSourceAfterUpdate.get( + Long deletedDocumentCountAfterUpdate = ((Number) syncJobSourceAfterUpdate.get( ConnectorSyncJob.DELETED_DOCUMENT_COUNT_FIELD.getPreferredName() - ); - Long indexedDocumentCountAfterUpdate = (Long) syncJobSourceAfterUpdate.get( + )).longValue(); + Long indexedDocumentCountAfterUpdate = ((Number) syncJobSourceAfterUpdate.get( ConnectorSyncJob.INDEXED_DOCUMENT_COUNT_FIELD.getPreferredName() - ); - Long indexedDocumentVolumeAfterUpdate = (Long) syncJobSourceAfterUpdate.get( + )).longValue(); + Long indexedDocumentVolumeAfterUpdate = ((Number) syncJobSourceAfterUpdate.get( ConnectorSyncJob.INDEXED_DOCUMENT_VOLUME_FIELD.getPreferredName() - ); - Long totalDocumentCountAfterUpdate = (Long) syncJobSourceAfterUpdate.get( + )).longValue(); + Long totalDocumentCountAfterUpdate = ((Number) syncJobSourceAfterUpdate.get( ConnectorSyncJob.TOTAL_DOCUMENT_COUNT_FIELD.getPreferredName() - ); + )).longValue(); Instant lastSeenAfterUpdate = Instant.parse( (String) syncJobSourceAfterUpdate.get(ConnectorSyncJob.LAST_SEEN_FIELD.getPreferredName()) ); @@ -1411,4 +1411,24 @@ private String updateConnectorSyncJobStatusWithoutStateMachineGuard(String syncJ // wait 10 seconds for connector creation return index.get(TIMEOUT_SECONDS, TimeUnit.SECONDS).getId(); } + + /** + * Test plugin to register the {@link ConnectorSyncJobIndexService} system index descriptor. + */ + public static class TestPlugin extends Plugin implements SystemIndexPlugin { + @Override + public Collection getSystemIndexDescriptors(Settings settings) { + return List.of(ConnectorSyncJobIndexService.getSystemIndexDescriptor()); + } + + @Override + public String getFeatureName() { + return this.getClass().getSimpleName(); + } + + @Override + public String getFeatureDescription() { + return this.getClass().getCanonicalName(); + } + } } diff --git a/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/connector/syncjob/ConnectorSyncJobTestUtils.java b/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/connector/syncjob/ConnectorSyncJobTestUtils.java index e72bf04fb7e55..1e6426051e04b 100644 --- a/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/connector/syncjob/ConnectorSyncJobTestUtils.java +++ b/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/connector/syncjob/ConnectorSyncJobTestUtils.java @@ -36,7 +36,7 @@ import static org.elasticsearch.test.ESTestCase.randomInt; import static org.elasticsearch.test.ESTestCase.randomLong; import static org.elasticsearch.test.ESTestCase.randomMap; -import static org.elasticsearch.test.ESTestCase.randomNonNegativeLong; +import static org.elasticsearch.test.ESTestCase.randomNonNegativeInt; public class ConnectorSyncJobTestUtils { @@ -51,11 +51,11 @@ public static ConnectorSyncJob getRandomConnectorSyncJob() { .setCompletedAt(randomFrom(new Instant[] { null, randomInstantBetween(lowerBoundInstant, upperBoundInstant) })) .setConnector(ConnectorTestUtils.getRandomSyncJobConnectorInfo()) .setCreatedAt(randomInstantBetween(lowerBoundInstant, upperBoundInstant)) - .setDeletedDocumentCount(randomLong()) + .setDeletedDocumentCount(randomNonNegativeInt()) .setError(randomFrom(new String[] { null, randomAlphaOfLength(10) })) .setId(randomAlphaOfLength(10)) - .setIndexedDocumentCount(randomLong()) - .setIndexedDocumentVolume(randomLong()) + .setIndexedDocumentCount(randomNonNegativeInt()) + .setIndexedDocumentVolume(randomNonNegativeInt()) .setJobType(getRandomConnectorJobType()) .setLastSeen(randomFrom(new Instant[] { null, randomInstantBetween(lowerBoundInstant, upperBoundInstant) })) .setMetadata( @@ -67,7 +67,7 @@ public static ConnectorSyncJob getRandomConnectorSyncJob() { ) .setStartedAt(randomFrom(new Instant[] { null, randomInstantBetween(lowerBoundInstant, upperBoundInstant) })) .setStatus(ConnectorTestUtils.getRandomSyncStatus()) - .setTotalDocumentCount(randomLong()) + .setTotalDocumentCount(randomNonNegativeInt()) .setTriggerMethod(getRandomConnectorSyncJobTriggerMethod()) .setWorkerHostname(randomAlphaOfLength(10)) .build(); @@ -156,10 +156,10 @@ public static UpdateConnectorSyncJobIngestionStatsAction.Request getRandomUpdate return new UpdateConnectorSyncJobIngestionStatsAction.Request( randomAlphaOfLength(10), - randomNonNegativeLong(), - randomNonNegativeLong(), - randomNonNegativeLong(), - randomNonNegativeLong(), + (long) randomNonNegativeInt(), + (long) randomNonNegativeInt(), + (long) randomNonNegativeInt(), + (long) randomNonNegativeInt(), randomInstantBetween(lowerBoundInstant, upperBoundInstant), randomMap(2, 3, () -> new Tuple<>(randomAlphaOfLength(4), randomAlphaOfLength(4))) ); @@ -173,10 +173,10 @@ public static UpdateConnectorSyncJobIngestionStatsAction.Request getRandomUpdate return new UpdateConnectorSyncJobIngestionStatsAction.Request( syncJobId, - randomNonNegativeLong(), - randomNonNegativeLong(), - randomNonNegativeLong(), - randomNonNegativeLong(), + (long) randomNonNegativeInt(), + (long) randomNonNegativeInt(), + (long) randomNonNegativeInt(), + (long) randomNonNegativeInt(), randomInstantBetween(lowerBoundInstant, upperBoundInstant), randomMap(2, 3, () -> new Tuple<>(randomAlphaOfLength(4), randomAlphaOfLength(4))) ); diff --git a/x-pack/plugin/esql/qa/security/src/javaRestTest/java/org/elasticsearch/xpack/esql/EsqlSecurityIT.java b/x-pack/plugin/esql/qa/security/src/javaRestTest/java/org/elasticsearch/xpack/esql/EsqlSecurityIT.java index 7d96c400cb659..a809bd50a45b8 100644 --- a/x-pack/plugin/esql/qa/security/src/javaRestTest/java/org/elasticsearch/xpack/esql/EsqlSecurityIT.java +++ b/x-pack/plugin/esql/qa/security/src/javaRestTest/java/org/elasticsearch/xpack/esql/EsqlSecurityIT.java @@ -57,6 +57,10 @@ public class EsqlSecurityIT extends ESRestTestCase { .user("user4", "x-pack-test-password", "user4", false) .user("user5", "x-pack-test-password", "user5", false) .user("fls_user", "x-pack-test-password", "fls_user", false) + .user("fls_user2", "x-pack-test-password", "fls_user2", false) + .user("fls_user3", "x-pack-test-password", "fls_user3", false) + .user("fls_user4_1", "x-pack-test-password", "fls_user4_1", false) + .user("dls_user", "x-pack-test-password", "dls_user", false) .user("metadata1_read2", "x-pack-test-password", "metadata1_read2", false) .user("alias_user1", "x-pack-test-password", "alias_user1", false) .user("alias_user2", "x-pack-test-password", "alias_user2", false) @@ -92,7 +96,7 @@ private void indexDocument(String index, int id, double value, String org) throw public void indexDocuments() throws IOException { Settings lookupSettings = Settings.builder().put("index.mode", "lookup").build(); String mapping = """ - "properties":{"value": {"type": "double"}, "org": {"type": "keyword"}} + "properties":{"value": {"type": "double"}, "org": {"type": "keyword"}, "other": {"type": "keyword"}} """; createIndex("index", Settings.EMPTY, mapping); @@ -163,6 +167,32 @@ public void indexDocuments() throws IOException { """); assertOK(client().performRequest(aliasRequest)); } + + createMultiRoleUsers(); + } + + private void createMultiRoleUsers() throws IOException { + Request request = new Request("POST", "_security/user/dls_user2"); + request.setJsonEntity(""" + { + "password" : "x-pack-test-password", + "roles" : [ "dls_user", "dls_user2" ], + "full_name" : "Test Role", + "email" : "test.role@example.com" + } + """); + assertOK(client().performRequest(request)); + + request = new Request("POST", "_security/user/fls_user4"); + request.setJsonEntity(""" + { + "password" : "x-pack-test-password", + "roles" : [ "fls_user4_1", "fls_user4_2" ], + "full_name" : "Test Role", + "email" : "test.role@example.com" + } + """); + assertOK(client().performRequest(request)); } protected MapMatcher responseMatcher(Map result) { @@ -538,12 +568,12 @@ record Listen(long timestamp, String songId, double duration) { public void testLookupJoinIndexAllowed() throws Exception { assumeTrue( "Requires LOOKUP JOIN capability", - EsqlSpecTestCase.hasCapabilities(adminClient(), List.of(EsqlCapabilities.Cap.JOIN_LOOKUP_V11.capabilityName())) + EsqlSpecTestCase.hasCapabilities(adminClient(), List.of(EsqlCapabilities.Cap.JOIN_LOOKUP_V12.capabilityName())) ); Response resp = runESQLCommand( "metadata1_read2", - "ROW x = 40.0 | EVAL value = x | LOOKUP JOIN `lookup-user2` ON value | KEEP x, org" + "ROW x = 40.0 | EVAL value = x | LOOKUP JOIN lookup-user2 ON value | KEEP x, org" ); assertOK(resp); Map respMap = entityAsMap(resp); @@ -553,36 +583,141 @@ public void testLookupJoinIndexAllowed() throws Exception { ); assertThat(respMap.get("values"), equalTo(List.of(List.of(40.0, "sales")))); - // Alias, should find the index and the row - resp = runESQLCommand("alias_user1", "ROW x = 31.0 | EVAL value = x | LOOKUP JOIN `lookup-first-alias` ON value | KEEP x, org"); + // Aliases are not allowed in LOOKUP JOIN + var resp2 = expectThrows( + ResponseException.class, + () -> runESQLCommand("alias_user1", "ROW x = 31.0 | EVAL value = x | LOOKUP JOIN lookup-first-alias ON value | KEEP x, org") + ); + + assertThat(resp2.getMessage(), containsString("Aliases and index patterns are not allowed for LOOKUP JOIN [lookup-first-alias]")); + assertThat(resp2.getResponse().getStatusLine().getStatusCode(), equalTo(HttpStatus.SC_BAD_REQUEST)); + + // Aliases are not allowed in LOOKUP JOIN, regardless of alias filters + resp2 = expectThrows( + ResponseException.class, + () -> runESQLCommand("alias_user1", "ROW x = 123.0 | EVAL value = x | LOOKUP JOIN lookup-first-alias ON value | KEEP x, org") + ); + assertThat(resp2.getMessage(), containsString("Aliases and index patterns are not allowed for LOOKUP JOIN [lookup-first-alias]")); + assertThat(resp2.getResponse().getStatusLine().getStatusCode(), equalTo(HttpStatus.SC_BAD_REQUEST)); + } + + @SuppressWarnings("unchecked") + public void testLookupJoinDocLevelSecurity() throws Exception { + assumeTrue( + "Requires LOOKUP JOIN capability", + EsqlSpecTestCase.hasCapabilities(adminClient(), List.of(EsqlCapabilities.Cap.JOIN_LOOKUP_V12.capabilityName())) + ); + + Response resp = runESQLCommand("dls_user", "ROW x = 40.0 | EVAL value = x | LOOKUP JOIN lookup-user2 ON value | KEEP x, org"); + assertOK(resp); + Map respMap = entityAsMap(resp); + assertThat( + respMap.get("columns"), + equalTo(List.of(Map.of("name", "x", "type", "double"), Map.of("name", "org", "type", "keyword"))) + ); + + assertThat(respMap.get("values"), equalTo(List.of(Arrays.asList(40.0, null)))); + + resp = runESQLCommand("dls_user", "ROW x = 32.0 | EVAL value = x | LOOKUP JOIN lookup-user2 ON value | KEEP x, org"); + assertOK(resp); + respMap = entityAsMap(resp); + assertThat( + respMap.get("columns"), + equalTo(List.of(Map.of("name", "x", "type", "double"), Map.of("name", "org", "type", "keyword"))) + ); + assertThat(respMap.get("values"), equalTo(List.of(List.of(32.0, "marketing")))); + + // same, but with a user that has two dls roles that allow him more visibility + + resp = runESQLCommand("dls_user2", "ROW x = 40.0 | EVAL value = x | LOOKUP JOIN lookup-user2 ON value | KEEP x, org"); assertOK(resp); respMap = entityAsMap(resp); assertThat( respMap.get("columns"), equalTo(List.of(Map.of("name", "x", "type", "double"), Map.of("name", "org", "type", "keyword"))) ); - assertThat(respMap.get("values"), equalTo(List.of(List.of(31.0, "sales")))); - // Alias, for a row that's filtered out - resp = runESQLCommand("alias_user1", "ROW x = 123.0 | EVAL value = x | LOOKUP JOIN `lookup-first-alias` ON value | KEEP x, org"); + assertThat(respMap.get("values"), equalTo(List.of(Arrays.asList(40.0, "sales")))); + + resp = runESQLCommand("dls_user2", "ROW x = 32.0 | EVAL value = x | LOOKUP JOIN lookup-user2 ON value | KEEP x, org"); assertOK(resp); respMap = entityAsMap(resp); assertThat( respMap.get("columns"), equalTo(List.of(Map.of("name", "x", "type", "double"), Map.of("name", "org", "type", "keyword"))) ); - assertThat(respMap.get("values"), equalTo(List.of(Arrays.asList(123.0, null)))); + assertThat(respMap.get("values"), equalTo(List.of(List.of(32.0, "marketing")))); + + } + + @SuppressWarnings("unchecked") + public void testLookupJoinFieldLevelSecurity() throws Exception { + assumeTrue( + "Requires LOOKUP JOIN capability", + EsqlSpecTestCase.hasCapabilities(adminClient(), List.of(EsqlCapabilities.Cap.JOIN_LOOKUP_V12.capabilityName())) + ); + + Response resp = runESQLCommand("fls_user2", "ROW x = 40.0 | EVAL value = x | LOOKUP JOIN lookup-user2 ON value"); + assertOK(resp); + Map respMap = entityAsMap(resp); + assertThat( + respMap.get("columns"), + equalTo( + List.of( + Map.of("name", "x", "type", "double"), + Map.of("name", "value", "type", "double"), + Map.of("name", "org", "type", "keyword") + ) + ) + ); + + resp = runESQLCommand("fls_user3", "ROW x = 40.0 | EVAL value = x | LOOKUP JOIN lookup-user2 ON value"); + assertOK(resp); + respMap = entityAsMap(resp); + assertThat( + respMap.get("columns"), + equalTo( + List.of( + Map.of("name", "x", "type", "double"), + Map.of("name", "value", "type", "double"), + Map.of("name", "org", "type", "keyword"), + Map.of("name", "other", "type", "keyword") + ) + ) + + ); + + resp = runESQLCommand("fls_user4", "ROW x = 40.0 | EVAL value = x | LOOKUP JOIN lookup-user2 ON value"); + assertOK(resp); + respMap = entityAsMap(resp); + assertThat( + respMap.get("columns"), + equalTo( + List.of( + Map.of("name", "x", "type", "double"), + Map.of("name", "value", "type", "double"), + Map.of("name", "org", "type", "keyword") + ) + ) + ); + + ResponseException error = expectThrows( + ResponseException.class, + () -> runESQLCommand("fls_user4_1", "ROW x = 40.0 | EVAL value = x | LOOKUP JOIN lookup-user2 ON value") + ); + assertThat(error.getMessage(), containsString("Unknown column [value] in right side of join")); + assertThat(error.getResponse().getStatusLine().getStatusCode(), equalTo(HttpStatus.SC_BAD_REQUEST)); } public void testLookupJoinIndexForbidden() throws Exception { assumeTrue( "Requires LOOKUP JOIN capability", - EsqlSpecTestCase.hasCapabilities(adminClient(), List.of(EsqlCapabilities.Cap.JOIN_LOOKUP_V11.capabilityName())) + EsqlSpecTestCase.hasCapabilities(adminClient(), List.of(EsqlCapabilities.Cap.JOIN_LOOKUP_V12.capabilityName())) ); var resp = expectThrows( ResponseException.class, - () -> runESQLCommand("metadata1_read2", "FROM lookup-user2 | EVAL value = 10.0 | LOOKUP JOIN `lookup-user1` ON value | KEEP x") + () -> runESQLCommand("metadata1_read2", "FROM lookup-user2 | EVAL value = 10.0 | LOOKUP JOIN lookup-user1 ON value | KEEP x") ); assertThat(resp.getMessage(), containsString("Unknown index [lookup-user1]")); assertThat(resp.getResponse().getStatusLine().getStatusCode(), equalTo(HttpStatus.SC_BAD_REQUEST)); @@ -591,7 +726,7 @@ public void testLookupJoinIndexForbidden() throws Exception { ResponseException.class, () -> runESQLCommand( "metadata1_read2", - "FROM lookup-user2 | EVAL value = 10.0 | LOOKUP JOIN `lookup-first-alias` ON value | KEEP x" + "FROM lookup-user2 | EVAL value = 10.0 | LOOKUP JOIN lookup-first-alias ON value | KEEP x" ) ); assertThat(resp.getMessage(), containsString("Unknown index [lookup-first-alias]")); @@ -599,14 +734,14 @@ public void testLookupJoinIndexForbidden() throws Exception { resp = expectThrows( ResponseException.class, - () -> runESQLCommand("metadata1_read2", "ROW x = 10.0 | EVAL value = x | LOOKUP JOIN `lookup-user1` ON value | KEEP x") + () -> runESQLCommand("metadata1_read2", "ROW x = 10.0 | EVAL value = x | LOOKUP JOIN lookup-user1 ON value | KEEP x") ); assertThat(resp.getMessage(), containsString("Unknown index [lookup-user1]")); assertThat(resp.getResponse().getStatusLine().getStatusCode(), equalTo(HttpStatus.SC_BAD_REQUEST)); resp = expectThrows( ResponseException.class, - () -> runESQLCommand("alias_user1", "ROW x = 10.0 | EVAL value = x | LOOKUP JOIN `lookup-user1` ON value | KEEP x") + () -> runESQLCommand("alias_user1", "ROW x = 10.0 | EVAL value = x | LOOKUP JOIN lookup-user1 ON value | KEEP x") ); assertThat(resp.getMessage(), containsString("Unknown index [lookup-user1]")); assertThat(resp.getResponse().getStatusLine().getStatusCode(), equalTo(HttpStatus.SC_BAD_REQUEST)); diff --git a/x-pack/plugin/esql/qa/security/src/javaRestTest/resources/roles.yml b/x-pack/plugin/esql/qa/security/src/javaRestTest/resources/roles.yml index f46e7ef56f3a1..745ae43cf640c 100644 --- a/x-pack/plugin/esql/qa/security/src/javaRestTest/resources/roles.yml +++ b/x-pack/plugin/esql/qa/security/src/javaRestTest/resources/roles.yml @@ -93,6 +93,53 @@ fls_user: field_security: grant: [ value ] +fls_user2: + cluster: [] + indices: + - names: [ 'lookup-user2' ] + privileges: [ 'read' ] + field_security: + grant: [ "org", "value" ] + +fls_user3: + cluster: [] + indices: + - names: [ 'lookup-user2' ] + privileges: [ 'read' ] + field_security: + grant: [ "org", "value", "other" ] + +fls_user4_1: + cluster: [] + indices: + - names: [ 'lookup-user2' ] + privileges: [ 'read' ] + field_security: + grant: [ "org" ] + +fls_user4_2: + cluster: [] + indices: + - names: [ 'lookup-user2' ] + privileges: [ 'read' ] + field_security: + grant: [ "value" ] + +dls_user: + cluster: [] + indices: + - names: [ 'lookup-user2' ] + privileges: [ 'read' ] + query: '{"match": {"org": "marketing"}}' + +dls_user2: + cluster: [] + indices: + - names: [ 'lookup-user2' ] + privileges: [ 'read' ] + query: '{"match": {"org": "sales"}}' + + logs_foo_all: cluster: [] indices: diff --git a/x-pack/plugin/esql/qa/server/mixed-cluster/src/javaRestTest/java/org/elasticsearch/xpack/esql/qa/mixed/MixedClusterEsqlSpecIT.java b/x-pack/plugin/esql/qa/server/mixed-cluster/src/javaRestTest/java/org/elasticsearch/xpack/esql/qa/mixed/MixedClusterEsqlSpecIT.java index 3b5377c2768fb..790b12346bb14 100644 --- a/x-pack/plugin/esql/qa/server/mixed-cluster/src/javaRestTest/java/org/elasticsearch/xpack/esql/qa/mixed/MixedClusterEsqlSpecIT.java +++ b/x-pack/plugin/esql/qa/server/mixed-cluster/src/javaRestTest/java/org/elasticsearch/xpack/esql/qa/mixed/MixedClusterEsqlSpecIT.java @@ -20,7 +20,7 @@ import java.util.List; import static org.elasticsearch.xpack.esql.CsvTestUtils.isEnabled; -import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.JOIN_LOOKUP_V11; +import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.JOIN_LOOKUP_V12; public class MixedClusterEsqlSpecIT extends EsqlSpecTestCase { @ClassRule @@ -82,7 +82,7 @@ protected boolean supportsInferenceTestService() { @Override protected boolean supportsIndexModeLookup() throws IOException { - return hasCapabilities(List.of(JOIN_LOOKUP_V11.capabilityName())); + return hasCapabilities(List.of(JOIN_LOOKUP_V12.capabilityName())); } @Override diff --git a/x-pack/plugin/esql/qa/server/multi-clusters/src/javaRestTest/java/org/elasticsearch/xpack/esql/ccq/MultiClusterSpecIT.java b/x-pack/plugin/esql/qa/server/multi-clusters/src/javaRestTest/java/org/elasticsearch/xpack/esql/ccq/MultiClusterSpecIT.java index f8b921f239923..4d06db94801bf 100644 --- a/x-pack/plugin/esql/qa/server/multi-clusters/src/javaRestTest/java/org/elasticsearch/xpack/esql/ccq/MultiClusterSpecIT.java +++ b/x-pack/plugin/esql/qa/server/multi-clusters/src/javaRestTest/java/org/elasticsearch/xpack/esql/ccq/MultiClusterSpecIT.java @@ -48,7 +48,7 @@ import static org.elasticsearch.xpack.esql.EsqlTestUtils.classpathResources; import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.INLINESTATS; import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.INLINESTATS_V2; -import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.JOIN_LOOKUP_V11; +import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.JOIN_LOOKUP_V12; import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.JOIN_PLANNING_V1; import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.METADATA_FIELDS_REMOTE_TEST; import static org.elasticsearch.xpack.esql.qa.rest.EsqlSpecTestCase.Mode.SYNC; @@ -124,7 +124,7 @@ protected void shouldSkipTest(String testName) throws IOException { assumeFalse("INLINESTATS not yet supported in CCS", testCase.requiredCapabilities.contains(INLINESTATS.capabilityName())); assumeFalse("INLINESTATS not yet supported in CCS", testCase.requiredCapabilities.contains(INLINESTATS_V2.capabilityName())); assumeFalse("INLINESTATS not yet supported in CCS", testCase.requiredCapabilities.contains(JOIN_PLANNING_V1.capabilityName())); - assumeFalse("LOOKUP JOIN not yet supported in CCS", testCase.requiredCapabilities.contains(JOIN_LOOKUP_V11.capabilityName())); + assumeFalse("LOOKUP JOIN not yet supported in CCS", testCase.requiredCapabilities.contains(JOIN_LOOKUP_V12.capabilityName())); } private TestFeatureService remoteFeaturesService() throws IOException { diff --git a/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/RequestIndexFilteringTestCase.java b/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/RequestIndexFilteringTestCase.java index ba057cbe276ba..94f6a3c65418d 100644 --- a/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/RequestIndexFilteringTestCase.java +++ b/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/RequestIndexFilteringTestCase.java @@ -211,7 +211,7 @@ public void testIndicesDontExist() throws IOException { assertThat(e.getMessage(), containsString("index_not_found_exception")); assertThat(e.getMessage(), anyOf(containsString("no such index [foo]"), containsString("no such index [remote_cluster:foo]"))); - if (EsqlCapabilities.Cap.JOIN_LOOKUP_V11.isEnabled()) { + if (EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()) { e = expectThrows( ResponseException.class, () -> runEsql(timestampFilter("gte", "2020-01-01").query(from("test1") + " | LOOKUP JOIN foo ON id1")) diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/lookup-join.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/lookup-join.csv-spec index 7b2395030a536..43d397c3d3764 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/lookup-join.csv-spec +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/lookup-join.csv-spec @@ -8,7 +8,7 @@ ############################################### basicOnTheDataNode -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM employees | EVAL language_code = languages @@ -25,7 +25,7 @@ emp_no:integer | language_code:integer | language_name:keyword ; basicRow -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 ROW language_code = 1 | LOOKUP JOIN languages_lookup ON language_code @@ -36,7 +36,7 @@ language_code:integer | language_name:keyword ; basicOnTheCoordinator -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM employees | SORT emp_no @@ -53,7 +53,7 @@ emp_no:integer | language_code:integer | language_name:keyword ; subsequentEvalOnTheDataNode -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM employees | EVAL language_code = languages @@ -71,7 +71,7 @@ emp_no:integer | language_code:integer | language_name:keyword | language_code_x ; subsequentEvalOnTheCoordinator -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM employees | SORT emp_no @@ -89,7 +89,7 @@ emp_no:integer | language_code:integer | language_name:keyword | language_code_x ; sortEvalBeforeLookup -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM employees | SORT emp_no @@ -106,7 +106,7 @@ emp_no:integer | language_code:integer | language_name:keyword ; repeatedIndexOnFrom -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM languages_lookup | LOOKUP JOIN languages_lookup ON language_code @@ -121,7 +121,7 @@ language_code:integer | language_name:keyword ; nonUniqueLeftKeyOnTheDataNode -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM employees | WHERE emp_no <= 10030 @@ -149,7 +149,7 @@ emp_no:integer | language_code:integer | language_name:keyword ########################################################################### nonUniqueRightKeyOnTheDataNode -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM employees | EVAL language_code = emp_no % 10 @@ -173,7 +173,7 @@ emp_no:integer | language_code:integer | language_name:keyword | country:text ; nonUniqueRightKeyOnTheCoordinator -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM employees | SORT emp_no @@ -199,7 +199,7 @@ emp_no:integer | language_code:integer | language_name:keyword | country:text nonUniqueRightKeyOnTheCoordinatorCorrectOrdering // Same as above, but don't ignore the order completely. At least the emp_no col must remain correctly ordered. -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM employees | SORT emp_no @@ -223,7 +223,7 @@ emp_no:integer | language_code:integer ; nonUniqueRightKeyFromRow -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 ROW language_code = 2 | LOOKUP JOIN languages_lookup_non_unique_key ON language_code @@ -238,7 +238,7 @@ language_code:integer | country:text | language_name:keyword ; keepFieldNotInLookupOnTheDataNode -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM employees | EVAL language_code = emp_no % 10 @@ -256,7 +256,7 @@ emp_no:integer ; dropAllFieldsUsedInLookupOnTheCoordinator -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM employees | WHERE emp_no == 10001 @@ -278,7 +278,7 @@ emp_no:integer ########################################################################### nullJoinKeyOnTheDataNode -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM employees | WHERE emp_no < 10004 @@ -300,7 +300,7 @@ emp_no:integer | language_code:integer | language_name:keyword ; mvJoinKeyOnTheLookupIndex -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM employees | WHERE 10003 < emp_no AND emp_no < 10008 @@ -319,7 +319,7 @@ emp_no:integer | language_code:integer | language_name:keyword ; mvJoinKeyOnFrom -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 required_capability: join_lookup_skip_mv FROM employees @@ -339,7 +339,7 @@ emp_no:integer | language_code:integer | language_name:keyword ; mvJoinKeyFromRow -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 required_capability: join_lookup_skip_mv ROW language_code = [4, 5, 6, 7] @@ -353,7 +353,7 @@ language_code:integer | language_name:keyword | country:text ; mvJoinKeyFromRowExpanded -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 ROW language_code = [4, 5, 6, 7, 8] | MV_EXPAND language_code @@ -376,7 +376,7 @@ language_code:integer | language_name:keyword | country:text ############################################### filterOnLeftSide -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM employees | EVAL language_code = languages @@ -393,7 +393,7 @@ emp_no:integer | language_code:integer | language_name:keyword ; filterOnRightSide -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM sample_data | LOOKUP JOIN message_types_lookup ON message @@ -409,7 +409,7 @@ FROM sample_data ; filterOnRightSideAfterStats -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM sample_data | LOOKUP JOIN message_types_lookup ON message @@ -422,7 +422,7 @@ count:long | type:keyword ; filterOnJoinKey -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM employees | EVAL language_code = languages @@ -437,7 +437,7 @@ emp_no:integer | language_code:integer | language_name:keyword ; filterOnJoinKeyAndRightSide -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM employees | WHERE emp_no < 10006 @@ -454,7 +454,7 @@ emp_no:integer | language_code:integer | language_name:keyword ; filterOnRightSideOnTheCoordinator -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM employees | SORT emp_no @@ -470,7 +470,7 @@ emp_no:integer | language_code:integer | language_name:keyword ; filterOnJoinKeyOnTheCoordinator -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM employees | SORT emp_no @@ -486,7 +486,7 @@ emp_no:integer | language_code:integer | language_name:keyword ; filterOnJoinKeyAndRightSideOnTheCoordinator -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM employees | SORT emp_no @@ -503,7 +503,7 @@ emp_no:integer | language_code:integer | language_name:keyword ; filterOnTheDataNodeThenFilterOnTheCoordinator -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM employees | EVAL language_code = languages @@ -524,7 +524,7 @@ emp_no:integer | language_code:integer | language_name:keyword ########################################################################### joinOnNestedField -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM employees | WHERE 10000 < emp_no AND emp_no < 10006 @@ -544,7 +544,7 @@ emp_no:integer | language.id:integer | language.name:text joinOnNestedFieldRow -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 ROW language.code = "EN" | LOOKUP JOIN languages_nested_fields ON language.code @@ -557,7 +557,7 @@ language.id:integer | language.code:keyword | language.name.keyword:keyword joinOnNestedNestedFieldRow -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 ROW language.name.keyword = "English" | LOOKUP JOIN languages_nested_fields ON language.name.keyword @@ -569,7 +569,7 @@ language.id:integer | language.name:text | language.name.keyword:keyword ; joinOnNestedNestedFieldRowExplicitKeyword -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 required_capability: lookup_join_text ROW language.name.keyword = "English" @@ -582,7 +582,7 @@ language.id:integer | language.name:text | language.name.keyword:keyword ; joinOnNestedNestedFieldRowExplicitKeywords -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 required_capability: lookup_join_text ROW language.name.keyword = ["English", "French"] @@ -601,7 +601,7 @@ language.id:integer | language.name:text | language.name.keyword:keyword | langu ############################################### lookupIPFromRow -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 ROW left = "left", client_ip = "172.21.0.5", right = "right" | LOOKUP JOIN clientips_lookup ON client_ip @@ -612,7 +612,7 @@ left | 172.21.0.5 | right | Development ; lookupIPFromKeepRow -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 ROW left = "left", client_ip = "172.21.0.5", right = "right" | KEEP left, client_ip, right @@ -624,7 +624,7 @@ left | 172.21.0.5 | right | Development ; lookupIPFromRowWithShadowing -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 ROW left = "left", client_ip = "172.21.0.5", env = "env", right = "right" | LOOKUP JOIN clientips_lookup ON client_ip @@ -635,7 +635,7 @@ left | 172.21.0.5 | right | Development ; lookupIPFromRowWithShadowingKeep -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 ROW left = "left", client_ip = "172.21.0.5", env = "env", right = "right" | EVAL client_ip = client_ip::keyword @@ -648,7 +648,7 @@ left | 172.21.0.5 | right | Development ; lookupIPFromRowWithShadowingKeepReordered -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 ROW left = "left", client_ip = "172.21.0.5", env = "env", right = "right" | EVAL client_ip = client_ip::keyword @@ -661,7 +661,7 @@ right | Development | 172.21.0.5 ; lookupIPFromIndex -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM sample_data | EVAL client_ip = client_ip::keyword @@ -680,7 +680,7 @@ ignoreOrder:true ; lookupIPFromIndexKeep -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM sample_data | EVAL client_ip = client_ip::keyword @@ -700,7 +700,7 @@ ignoreOrder:true ; lookupIPFromIndexKeepKeep -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM sample_data | KEEP client_ip, event_duration, @timestamp, message @@ -722,7 +722,7 @@ timestamp:date | client_ip:keyword | event_duration:long | msg:keyword ; lookupIPFromIndexStats -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM sample_data | EVAL client_ip = client_ip::keyword @@ -738,7 +738,7 @@ count:long | env:keyword ; lookupIPFromIndexStatsKeep -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM sample_data | EVAL client_ip = client_ip::keyword @@ -755,7 +755,7 @@ count:long | env:keyword ; statsAndLookupIPFromIndex -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM sample_data | EVAL client_ip = client_ip::keyword @@ -776,7 +776,7 @@ count:long | client_ip:keyword | env:keyword ############################################### lookupMessageFromRow -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 ROW left = "left", message = "Connected to 10.1.0.1", right = "right" | LOOKUP JOIN message_types_lookup ON message @@ -787,7 +787,7 @@ left | Connected to 10.1.0.1 | right | Success ; lookupMessageFromKeepRow -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 ROW left = "left", message = "Connected to 10.1.0.1", right = "right" | KEEP left, message, right @@ -799,7 +799,7 @@ left | Connected to 10.1.0.1 | right | Success ; lookupMessageFromRowWithShadowing -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 ROW left = "left", message = "Connected to 10.1.0.1", type = "unknown", right = "right" | LOOKUP JOIN message_types_lookup ON message @@ -810,7 +810,7 @@ left | Connected to 10.1.0.1 | right | Success ; lookupMessageFromRowWithShadowingKeep -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 ROW left = "left", message = "Connected to 10.1.0.1", type = "unknown", right = "right" | LOOKUP JOIN message_types_lookup ON message @@ -822,7 +822,7 @@ left | Connected to 10.1.0.1 | right | Success ; lookupMessageFromIndex -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM sample_data | LOOKUP JOIN message_types_lookup ON message @@ -840,7 +840,7 @@ ignoreOrder:true ; lookupMessageFromIndexKeep -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM sample_data | LOOKUP JOIN message_types_lookup ON message @@ -859,7 +859,7 @@ ignoreOrder:true ; lookupMessageFromIndexKeepKeep -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM sample_data | KEEP client_ip, event_duration, @timestamp, message @@ -879,7 +879,7 @@ ignoreOrder:true ; lookupMessageFromIndexKeepReordered -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM sample_data | LOOKUP JOIN message_types_lookup ON message @@ -898,7 +898,7 @@ Success | 172.21.2.162 | 3450233 | Connected to 10.1.0.3 ; lookupMessageFromIndexStats -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM sample_data | LOOKUP JOIN message_types_lookup ON message @@ -913,7 +913,7 @@ count:long | type:keyword ; lookupMessageFromIndexStatsKeep -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM sample_data | LOOKUP JOIN message_types_lookup ON message @@ -929,7 +929,7 @@ count:long | type:keyword ; statsAndLookupMessageFromIndex -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM sample_data | STATS count = count(message) BY message @@ -947,7 +947,7 @@ count:long | type:keyword | message:keyword ; lookupMessageFromIndexTwice -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM sample_data | LOOKUP JOIN message_types_lookup ON message @@ -969,7 +969,7 @@ ignoreOrder:true ; lookupMessageFromIndexTwiceKeep -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM sample_data | LOOKUP JOIN message_types_lookup ON message @@ -992,7 +992,7 @@ ignoreOrder:true ; lookupMessageFromIndexTwiceFullyShadowing -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM sample_data | LOOKUP JOIN message_types_lookup ON message @@ -1016,7 +1016,7 @@ ignoreOrder:true ############################################### lookupIPAndMessageFromRow -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 ROW left = "left", client_ip = "172.21.0.5", message = "Connected to 10.1.0.1", right = "right" | LOOKUP JOIN clientips_lookup ON client_ip @@ -1028,7 +1028,7 @@ left | 172.21.0.5 | Connected to 10.1.0.1 | right | Devel ; lookupIPAndMessageFromRowKeepBefore -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 ROW left = "left", client_ip = "172.21.0.5", message = "Connected to 10.1.0.1", right = "right" | KEEP left, client_ip, message, right @@ -1041,7 +1041,7 @@ left | 172.21.0.5 | Connected to 10.1.0.1 | right | Devel ; lookupIPAndMessageFromRowKeepBetween -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 ROW left = "left", client_ip = "172.21.0.5", message = "Connected to 10.1.0.1", right = "right" | LOOKUP JOIN clientips_lookup ON client_ip @@ -1054,7 +1054,7 @@ left | 172.21.0.5 | Connected to 10.1.0.1 | right | Devel ; lookupIPAndMessageFromRowKeepAfter -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 ROW left = "left", client_ip = "172.21.0.5", message = "Connected to 10.1.0.1", right = "right" | LOOKUP JOIN clientips_lookup ON client_ip @@ -1067,7 +1067,7 @@ left | 172.21.0.5 | Connected to 10.1.0.1 | right | Devel ; lookupIPAndMessageFromRowWithShadowing -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 ROW left = "left", client_ip = "172.21.0.5", message = "Connected to 10.1.0.1", env = "env", type = "type", right = "right" | LOOKUP JOIN clientips_lookup ON client_ip @@ -1079,7 +1079,7 @@ left | 172.21.0.5 | Connected to 10.1.0.1 | right | Devel ; lookupIPAndMessageFromRowWithShadowingKeep -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 ROW left = "left", client_ip = "172.21.0.5", message = "Connected to 10.1.0.1", env = "env", right = "right" | EVAL client_ip = client_ip::keyword @@ -1093,7 +1093,7 @@ left | 172.21.0.5 | Connected to 10.1.0.1 | right | Devel ; lookupIPAndMessageFromRowWithShadowingKeepKeep -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 ROW left = "left", client_ip = "172.21.0.5", message = "Connected to 10.1.0.1", env = "env", right = "right" | EVAL client_ip = client_ip::keyword @@ -1108,7 +1108,7 @@ left | 172.21.0.5 | Connected to 10.1.0.1 | right | Devel ; lookupIPAndMessageFromRowWithShadowingKeepKeepKeep -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 ROW left = "left", client_ip = "172.21.0.5", message = "Connected to 10.1.0.1", env = "env", right = "right" | EVAL client_ip = client_ip::keyword @@ -1124,7 +1124,7 @@ left | 172.21.0.5 | Connected to 10.1.0.1 | right | Devel ; lookupIPAndMessageFromRowWithShadowingKeepReordered -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 ROW left = "left", client_ip = "172.21.0.5", message = "Connected to 10.1.0.1", env = "env", right = "right" | EVAL client_ip = client_ip::keyword @@ -1138,7 +1138,7 @@ right | Development | Success | 172.21.0.5 ; lookupIPAndMessageFromIndex -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM sample_data | EVAL client_ip = client_ip::keyword @@ -1158,7 +1158,7 @@ ignoreOrder:true ; lookupIPAndMessageFromIndexKeep -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM sample_data | EVAL client_ip = client_ip::keyword @@ -1179,7 +1179,7 @@ ignoreOrder:true ; lookupIPAndMessageFromIndexStats -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM sample_data | EVAL client_ip = client_ip::keyword @@ -1197,7 +1197,7 @@ count:long | env:keyword | type:keyword ; lookupIPAndMessageFromIndexStatsKeep -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM sample_data | EVAL client_ip = client_ip::keyword @@ -1216,7 +1216,7 @@ count:long | env:keyword | type:keyword ; statsAndLookupIPAndMessageFromIndex -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM sample_data | EVAL client_ip = client_ip::keyword @@ -1235,7 +1235,7 @@ count:long | client_ip:keyword | message:keyword | env:keyword | type:keyw ; lookupIPAndMessageFromIndexChainedEvalKeep -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM sample_data | EVAL client_ip = client_ip::keyword @@ -1257,7 +1257,7 @@ ignoreOrder:true ; lookupIPAndMessageFromIndexChainedRenameKeep -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM sample_data | EVAL client_ip = client_ip::keyword @@ -1280,7 +1280,7 @@ ignoreOrder:true lookupIndexInFromRepeatedRowBug // Test for https://github.com/elastic/elasticsearch/issues/118852 -required_capability: join_lookup_v11 +required_capability: join_lookup_v12 FROM languages_lookup_non_unique_key | WHERE language_code == 1 | LOOKUP JOIN languages_lookup ON language_code @@ -1294,3 +1294,20 @@ language_code:integer | language_name:keyword | country:text 1 | English | United States of America 1 | English | null ; + +lookupIndexQuoting +required_capability: join_lookup_v12 +FROM languages_lookup_non_unique_key +| WHERE language_code == 1 +| LOOKUP JOIN "languages_lookup" ON language_code +| LOOKUP JOIN """languages_lookup""" ON language_code +| KEEP language_code, language_name, country +| SORT language_code, language_name, country +; + +language_code:integer | language_name:keyword | country:text +1 | English | Canada +1 | English | United Kingdom +1 | English | United States of America +1 | English | null +; diff --git a/x-pack/plugin/esql/src/main/antlr/EsqlBaseLexer.g4 b/x-pack/plugin/esql/src/main/antlr/EsqlBaseLexer.g4 index 72fb491cdd982..12d990550f0f9 100644 --- a/x-pack/plugin/esql/src/main/antlr/EsqlBaseLexer.g4 +++ b/x-pack/plugin/esql/src/main/antlr/EsqlBaseLexer.g4 @@ -561,6 +561,10 @@ JOIN_AS : AS -> type(AS); JOIN_ON : ON -> type(ON), popMode, pushMode(EXPRESSION_MODE); USING : 'USING' -> popMode, pushMode(EXPRESSION_MODE); +JOIN_UNQUOTED_SOURCE: UNQUOTED_SOURCE -> type(UNQUOTED_SOURCE); +JOIN_QUOTED_SOURCE : QUOTED_STRING -> type(QUOTED_STRING); +JOIN_COLON : COLON -> type(COLON); + JOIN_UNQUOTED_IDENTIFER: UNQUOTED_IDENTIFIER -> type(UNQUOTED_IDENTIFIER); JOIN_QUOTED_IDENTIFIER : QUOTED_IDENTIFIER -> type(QUOTED_IDENTIFIER); diff --git a/x-pack/plugin/esql/src/main/antlr/EsqlBaseParser.g4 b/x-pack/plugin/esql/src/main/antlr/EsqlBaseParser.g4 index e12904a25b131..e72c0fdafd73c 100644 --- a/x-pack/plugin/esql/src/main/antlr/EsqlBaseParser.g4 +++ b/x-pack/plugin/esql/src/main/antlr/EsqlBaseParser.g4 @@ -328,7 +328,7 @@ joinCommand ; joinTarget - : index=identifier (AS alias=identifier)? + : index=indexPattern (AS alias=identifier)? ; joinCondition diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java index e4c591f8f6b19..182328b54c4c5 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java @@ -691,7 +691,7 @@ public enum Cap { /** * LOOKUP JOIN */ - JOIN_LOOKUP_V11(Build.current().isSnapshot()), + JOIN_LOOKUP_V12(Build.current().isSnapshot()), /** * LOOKUP JOIN with TEXT fields on the right (right side of the join) (#119473) @@ -701,7 +701,7 @@ public enum Cap { /** * LOOKUP JOIN without MV matching (https://github.com/elastic/elasticsearch/issues/118780) */ - JOIN_LOOKUP_SKIP_MV(JOIN_LOOKUP_V11.isEnabled()), + JOIN_LOOKUP_SKIP_MV(JOIN_LOOKUP_V12.isEnabled()), /** * Fix for https://github.com/elastic/elasticsearch/issues/117054 @@ -751,7 +751,12 @@ public enum Cap { /** * Support named argument for function in map format. */ - OPTIONAL_NAMED_ARGUMENT_MAP_FOR_FUNCTION(Build.current().isSnapshot()); + OPTIONAL_NAMED_ARGUMENT_MAP_FOR_FUNCTION(Build.current().isSnapshot()), + + /** + * Disabled support for index aliases in lookup joins + */ + LOOKUP_JOIN_NO_ALIASES(JOIN_LOOKUP_V12.isEnabled()); private final boolean enabled; diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java index fc1b7f6329ab3..4f5ff35b84054 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java @@ -64,7 +64,7 @@ import org.elasticsearch.xpack.esql.index.EsIndex; import org.elasticsearch.xpack.esql.index.IndexResolution; import org.elasticsearch.xpack.esql.parser.ParsingException; -import org.elasticsearch.xpack.esql.plan.TableIdentifier; +import org.elasticsearch.xpack.esql.plan.IndexPattern; import org.elasticsearch.xpack.esql.plan.logical.Aggregate; import org.elasticsearch.xpack.esql.plan.logical.Drop; import org.elasticsearch.xpack.esql.plan.logical.Enrich; @@ -202,7 +202,9 @@ private static class ResolveTable extends ParameterizedAnalyzerRule { List list = p.indexMode() == IndexMode.LOOKUP ? lookupIndices : indices; - list.add(new TableInfo(p.table())); + list.add(new TableInfo(p.indexPattern())); }); plan.forEachUp(Enrich.class, unresolvedEnriches::add); diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/TableInfo.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/TableInfo.java index eff658e8997b0..38d368bd2bfad 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/TableInfo.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/TableInfo.java @@ -7,17 +7,17 @@ package org.elasticsearch.xpack.esql.analysis; -import org.elasticsearch.xpack.esql.plan.TableIdentifier; +import org.elasticsearch.xpack.esql.plan.IndexPattern; public class TableInfo { - private final TableIdentifier id; + private final IndexPattern id; - public TableInfo(TableIdentifier id) { + public TableInfo(IndexPattern id) { this.id = id; } - public TableIdentifier id() { + public IndexPattern id() { return id; } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/AbstractLookupService.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/AbstractLookupService.java index 961d74794961f..cb2582db2ad33 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/AbstractLookupService.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/AbstractLookupService.java @@ -12,7 +12,6 @@ import org.elasticsearch.action.IndicesRequest; import org.elasticsearch.action.UnavailableShardsException; import org.elasticsearch.action.support.ChannelActionListener; -import org.elasticsearch.action.support.ContextPreservingActionListener; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.node.DiscoveryNode; @@ -23,7 +22,6 @@ import org.elasticsearch.common.CheckedBiFunction; import org.elasticsearch.common.collect.Iterators; import org.elasticsearch.common.io.stream.StreamInput; -import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.compute.data.Block; @@ -67,15 +65,6 @@ import org.elasticsearch.transport.TransportRequestOptions; import org.elasticsearch.transport.TransportResponse; import org.elasticsearch.transport.TransportService; -import org.elasticsearch.xpack.core.ClientHelper; -import org.elasticsearch.xpack.core.XPackSettings; -import org.elasticsearch.xpack.core.security.SecurityContext; -import org.elasticsearch.xpack.core.security.action.user.HasPrivilegesAction; -import org.elasticsearch.xpack.core.security.action.user.HasPrivilegesRequest; -import org.elasticsearch.xpack.core.security.action.user.HasPrivilegesResponse; -import org.elasticsearch.xpack.core.security.authz.RoleDescriptor; -import org.elasticsearch.xpack.core.security.support.Exceptions; -import org.elasticsearch.xpack.core.security.user.User; import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException; import org.elasticsearch.xpack.esql.core.expression.Alias; import org.elasticsearch.xpack.esql.core.expression.NamedExpression; @@ -93,7 +82,6 @@ import java.util.Objects; import java.util.concurrent.Executor; import java.util.function.Function; -import java.util.stream.Collectors; import java.util.stream.IntStream; /** @@ -132,10 +120,10 @@ */ public abstract class AbstractLookupService { private final String actionName; - private final ClusterService clusterService; + protected final ClusterService clusterService; private final LookupShardContextFactory lookupShardContextFactory; - private final TransportService transportService; - private final Executor executor; + protected final TransportService transportService; + protected final Executor executor; private final BigArrays bigArrays; private final BlockFactory blockFactory; private final LocalCircuitBreaker.SizeSettings localBreakerSettings; @@ -218,97 +206,43 @@ protected static QueryList termQueryList( * Perform the actual lookup. */ public final void lookupAsync(R request, CancellableTask parentTask, ActionListener> outListener) { - ThreadContext threadContext = transportService.getThreadPool().getThreadContext(); - ActionListener> listener = ContextPreservingActionListener.wrapPreservingContext(outListener, threadContext); - hasPrivilege(listener.delegateFailureAndWrap((delegate, ignored) -> { - ClusterState clusterState = clusterService.state(); - GroupShardsIterator shardIterators = clusterService.operationRouting() - .searchShards(clusterState, new String[] { request.index }, Map.of(), "_local"); - if (shardIterators.size() != 1) { - delegate.onFailure(new EsqlIllegalArgumentException("target index {} has more than one shard", request.index)); - return; - } - ShardIterator shardIt = shardIterators.get(0); - ShardRouting shardRouting = shardIt.nextOrNull(); - ShardId shardId = shardIt.shardId(); - if (shardRouting == null) { - delegate.onFailure(new UnavailableShardsException(shardId, "target index is not available")); - return; - } - DiscoveryNode targetNode = clusterState.nodes().get(shardRouting.currentNodeId()); - T transportRequest = transportRequest(request, shardId); - // TODO: handle retry and avoid forking for the local lookup - try (ThreadContext.StoredContext unused = threadContext.stashWithOrigin(ClientHelper.ENRICH_ORIGIN)) { - transportService.sendChildRequest( - targetNode, - actionName, - transportRequest, - parentTask, - TransportRequestOptions.EMPTY, - new ActionListenerResponseHandler<>( - delegate.map(LookupResponse::takePages), - in -> readLookupResponse(in, blockFactory), - executor - ) - ); - } - })); - } - - /** - * Get the privilege required to perform the lookup. - *

- * If null is returned, no privilege check will be performed. - *

- */ - @Nullable - protected abstract String getRequiredPrivilege(); - - private void hasPrivilege(ActionListener outListener) { - final Settings settings = clusterService.getSettings(); - String privilegeName = getRequiredPrivilege(); - if (privilegeName == null - || settings.hasValue(XPackSettings.SECURITY_ENABLED.getKey()) == false - || XPackSettings.SECURITY_ENABLED.get(settings) == false) { - outListener.onResponse(null); + ClusterState clusterState = clusterService.state(); + GroupShardsIterator shardIterators = clusterService.operationRouting() + .searchShards(clusterState, new String[] { request.index }, Map.of(), "_local"); + if (shardIterators.size() != 1) { + outListener.onFailure(new EsqlIllegalArgumentException("target index {} has more than one shard", request.index)); return; } - final ThreadContext threadContext = transportService.getThreadPool().getThreadContext(); - final SecurityContext securityContext = new SecurityContext(Settings.EMPTY, threadContext); - final User user = securityContext.getUser(); - if (user == null) { - outListener.onFailure(new IllegalStateException("missing or unable to read authentication info on request")); + ShardIterator shardIt = shardIterators.get(0); + ShardRouting shardRouting = shardIt.nextOrNull(); + ShardId shardId = shardIt.shardId(); + if (shardRouting == null) { + outListener.onFailure(new UnavailableShardsException(shardId, "target index is not available")); return; } - HasPrivilegesRequest request = new HasPrivilegesRequest(); - request.username(user.principal()); - request.clusterPrivileges(privilegeName); - request.indexPrivileges(new RoleDescriptor.IndicesPrivileges[0]); - request.applicationPrivileges(new RoleDescriptor.ApplicationResourcePrivileges[0]); - ActionListener listener = outListener.delegateFailureAndWrap((l, resp) -> { - if (resp.isCompleteMatch()) { - l.onResponse(null); - return; - } - String detailed = resp.getClusterPrivileges() - .entrySet() - .stream() - .filter(e -> e.getValue() == false) - .map(e -> "privilege [" + e.getKey() + "] is missing") - .collect(Collectors.joining(", ")); - String message = "user [" - + user.principal() - + "] doesn't have " - + "sufficient privileges to perform enrich lookup: " - + detailed; - l.onFailure(Exceptions.authorizationError(message)); - }); - transportService.sendRequest( - transportService.getLocalNode(), - HasPrivilegesAction.NAME, - request, + DiscoveryNode targetNode = clusterState.nodes().get(shardRouting.currentNodeId()); + T transportRequest = transportRequest(request, shardId); + // TODO: handle retry and avoid forking for the local lookup + sendChildRequest(parentTask, outListener, targetNode, transportRequest); + } + + protected void sendChildRequest( + CancellableTask parentTask, + ActionListener> delegate, + DiscoveryNode targetNode, + T transportRequest + ) { + transportService.sendChildRequest( + targetNode, + actionName, + transportRequest, + parentTask, TransportRequestOptions.EMPTY, - new ActionListenerResponseHandler<>(listener, HasPrivilegesResponse::new, executor) + new ActionListenerResponseHandler<>( + delegate.map(LookupResponse::takePages), + in -> readLookupResponse(in, blockFactory), + executor + ) ); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichLookupService.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichLookupService.java index acb4206ad7af8..480b69ecd8e60 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichLookupService.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichLookupService.java @@ -8,10 +8,16 @@ package org.elasticsearch.xpack.esql.enrich; import org.elasticsearch.TransportVersions; +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.ActionListenerResponseHandler; +import org.elasticsearch.action.support.ContextPreservingActionListener; +import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.BigArrays; +import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.BlockStreamInput; @@ -23,9 +29,20 @@ import org.elasticsearch.index.mapper.RangeType; import org.elasticsearch.index.query.SearchExecutionContext; import org.elasticsearch.index.shard.ShardId; +import org.elasticsearch.tasks.CancellableTask; import org.elasticsearch.tasks.TaskId; +import org.elasticsearch.transport.TransportRequestOptions; import org.elasticsearch.transport.TransportService; +import org.elasticsearch.xpack.core.ClientHelper; +import org.elasticsearch.xpack.core.XPackSettings; +import org.elasticsearch.xpack.core.security.SecurityContext; +import org.elasticsearch.xpack.core.security.action.user.HasPrivilegesAction; +import org.elasticsearch.xpack.core.security.action.user.HasPrivilegesRequest; +import org.elasticsearch.xpack.core.security.action.user.HasPrivilegesResponse; +import org.elasticsearch.xpack.core.security.authz.RoleDescriptor; import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilegeResolver; +import org.elasticsearch.xpack.core.security.support.Exceptions; +import org.elasticsearch.xpack.core.security.user.User; import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException; import org.elasticsearch.xpack.esql.action.EsqlQueryAction; import org.elasticsearch.xpack.esql.core.expression.NamedExpression; @@ -36,6 +53,7 @@ import java.io.IOException; import java.util.List; +import java.util.stream.Collectors; /** * {@link EnrichLookupService} performs enrich lookup for a given input page. @@ -90,11 +108,6 @@ protected QueryList queryList(TransportRequest request, SearchExecutionContext c }; } - @Override - protected String getRequiredPrivilege() { - return ClusterPrivilegeResolver.MONITOR_ENRICH.name(); - } - @Override protected LookupResponse createLookupResponse(List pages, BlockFactory blockFactory) throws IOException { if (pages.size() != 1) { @@ -270,4 +283,70 @@ protected void innerRelease() { } } } + + @Override + protected void sendChildRequest( + CancellableTask parentTask, + ActionListener> delegate, + DiscoveryNode targetNode, + TransportRequest transportRequest + ) { + ThreadContext threadContext = transportService.getThreadPool().getThreadContext(); + ActionListener> listener = ContextPreservingActionListener.wrapPreservingContext(delegate, threadContext); + hasEnrichPrivilege(listener.delegateFailureAndWrap((l, ignored) -> { + // Since we just checked the needed privileges + // we can access the index regardless of the user/role that is executing the query + try (ThreadContext.StoredContext unused = threadContext.stashWithOrigin(ClientHelper.ENRICH_ORIGIN)) { + super.sendChildRequest(parentTask, l, targetNode, transportRequest); + } + })); + } + + protected void hasEnrichPrivilege(ActionListener outListener) { + final Settings settings = clusterService.getSettings(); + String privilegeName = ClusterPrivilegeResolver.MONITOR_ENRICH.name(); + if (privilegeName == null + || settings.hasValue(XPackSettings.SECURITY_ENABLED.getKey()) == false + || XPackSettings.SECURITY_ENABLED.get(settings) == false) { + outListener.onResponse(null); + return; + } + final ThreadContext threadContext = transportService.getThreadPool().getThreadContext(); + final SecurityContext securityContext = new SecurityContext(Settings.EMPTY, threadContext); + final User user = securityContext.getUser(); + if (user == null) { + outListener.onFailure(new IllegalStateException("missing or unable to read authentication info on request")); + return; + } + HasPrivilegesRequest request = new HasPrivilegesRequest(); + request.username(user.principal()); + request.clusterPrivileges(privilegeName); + request.indexPrivileges(new RoleDescriptor.IndicesPrivileges[0]); + request.applicationPrivileges(new RoleDescriptor.ApplicationResourcePrivileges[0]); + ActionListener listener = outListener.delegateFailureAndWrap((l, resp) -> { + if (resp.isCompleteMatch()) { + l.onResponse(null); + return; + } + String detailed = resp.getClusterPrivileges() + .entrySet() + .stream() + .filter(e -> e.getValue() == false) + .map(e -> "privilege [" + e.getKey() + "] is missing") + .collect(Collectors.joining(", ")); + String message = "user [" + + user.principal() + + "] doesn't have " + + "sufficient privileges to perform enrich lookup: " + + detailed; + l.onFailure(Exceptions.authorizationError(message)); + }); + transportService.sendRequest( + transportService.getLocalNode(), + HasPrivilegesAction.NAME, + request, + TransportRequestOptions.EMPTY, + new ActionListenerResponseHandler<>(listener, HasPrivilegesResponse::new, executor) + ); + } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/LookupFromIndexService.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/LookupFromIndexService.java index 9bea212a56aa8..131d8ddfa5ccd 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/LookupFromIndexService.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/LookupFromIndexService.java @@ -90,11 +90,6 @@ protected AbstractLookupService.LookupResponse readLookupResponse(StreamInput in return new LookupResponse(in, blockFactory); } - @Override - protected String getRequiredPrivilege() { - return null; - } - public static class Request extends AbstractLookupService.Request { private final String matchField; diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseLexer.interp b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseLexer.interp index 256bb094b45b7..92274ebe15513 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseLexer.interp +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseLexer.interp @@ -460,6 +460,9 @@ JOIN_JOIN JOIN_AS JOIN_ON USING +JOIN_UNQUOTED_SOURCE +JOIN_QUOTED_SOURCE +JOIN_COLON JOIN_UNQUOTED_IDENTIFER JOIN_QUOTED_IDENTIFIER JOIN_LINE_COMMENT @@ -504,4 +507,4 @@ METRICS_MODE CLOSING_METRICS_MODE atn: -[4, 0, 130, 1611, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67, 2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2, 73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 2, 78, 7, 78, 2, 79, 7, 79, 2, 80, 7, 80, 2, 81, 7, 81, 2, 82, 7, 82, 2, 83, 7, 83, 2, 84, 7, 84, 2, 85, 7, 85, 2, 86, 7, 86, 2, 87, 7, 87, 2, 88, 7, 88, 2, 89, 7, 89, 2, 90, 7, 90, 2, 91, 7, 91, 2, 92, 7, 92, 2, 93, 7, 93, 2, 94, 7, 94, 2, 95, 7, 95, 2, 96, 7, 96, 2, 97, 7, 97, 2, 98, 7, 98, 2, 99, 7, 99, 2, 100, 7, 100, 2, 101, 7, 101, 2, 102, 7, 102, 2, 103, 7, 103, 2, 104, 7, 104, 2, 105, 7, 105, 2, 106, 7, 106, 2, 107, 7, 107, 2, 108, 7, 108, 2, 109, 7, 109, 2, 110, 7, 110, 2, 111, 7, 111, 2, 112, 7, 112, 2, 113, 7, 113, 2, 114, 7, 114, 2, 115, 7, 115, 2, 116, 7, 116, 2, 117, 7, 117, 2, 118, 7, 118, 2, 119, 7, 119, 2, 120, 7, 120, 2, 121, 7, 121, 2, 122, 7, 122, 2, 123, 7, 123, 2, 124, 7, 124, 2, 125, 7, 125, 2, 126, 7, 126, 2, 127, 7, 127, 2, 128, 7, 128, 2, 129, 7, 129, 2, 130, 7, 130, 2, 131, 7, 131, 2, 132, 7, 132, 2, 133, 7, 133, 2, 134, 7, 134, 2, 135, 7, 135, 2, 136, 7, 136, 2, 137, 7, 137, 2, 138, 7, 138, 2, 139, 7, 139, 2, 140, 7, 140, 2, 141, 7, 141, 2, 142, 7, 142, 2, 143, 7, 143, 2, 144, 7, 144, 2, 145, 7, 145, 2, 146, 7, 146, 2, 147, 7, 147, 2, 148, 7, 148, 2, 149, 7, 149, 2, 150, 7, 150, 2, 151, 7, 151, 2, 152, 7, 152, 2, 153, 7, 153, 2, 154, 7, 154, 2, 155, 7, 155, 2, 156, 7, 156, 2, 157, 7, 157, 2, 158, 7, 158, 2, 159, 7, 159, 2, 160, 7, 160, 2, 161, 7, 161, 2, 162, 7, 162, 2, 163, 7, 163, 2, 164, 7, 164, 2, 165, 7, 165, 2, 166, 7, 166, 2, 167, 7, 167, 2, 168, 7, 168, 2, 169, 7, 169, 2, 170, 7, 170, 2, 171, 7, 171, 2, 172, 7, 172, 2, 173, 7, 173, 2, 174, 7, 174, 2, 175, 7, 175, 2, 176, 7, 176, 2, 177, 7, 177, 2, 178, 7, 178, 2, 179, 7, 179, 2, 180, 7, 180, 2, 181, 7, 181, 2, 182, 7, 182, 2, 183, 7, 183, 2, 184, 7, 184, 2, 185, 7, 185, 2, 186, 7, 186, 2, 187, 7, 187, 2, 188, 7, 188, 2, 189, 7, 189, 2, 190, 7, 190, 2, 191, 7, 191, 2, 192, 7, 192, 2, 193, 7, 193, 2, 194, 7, 194, 2, 195, 7, 195, 2, 196, 7, 196, 2, 197, 7, 197, 2, 198, 7, 198, 2, 199, 7, 199, 2, 200, 7, 200, 2, 201, 7, 201, 2, 202, 7, 202, 2, 203, 7, 203, 2, 204, 7, 204, 2, 205, 7, 205, 2, 206, 7, 206, 2, 207, 7, 207, 2, 208, 7, 208, 2, 209, 7, 209, 2, 210, 7, 210, 2, 211, 7, 211, 2, 212, 7, 212, 2, 213, 7, 213, 2, 214, 7, 214, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 24, 4, 24, 658, 8, 24, 11, 24, 12, 24, 659, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 1, 25, 5, 25, 668, 8, 25, 10, 25, 12, 25, 671, 9, 25, 1, 25, 3, 25, 674, 8, 25, 1, 25, 3, 25, 677, 8, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 5, 26, 686, 8, 26, 10, 26, 12, 26, 689, 9, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 27, 4, 27, 697, 8, 27, 11, 27, 12, 27, 698, 1, 27, 1, 27, 1, 28, 1, 28, 1, 28, 1, 28, 1, 29, 1, 29, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 1, 32, 1, 32, 1, 33, 1, 33, 3, 33, 718, 8, 33, 1, 33, 4, 33, 721, 8, 33, 11, 33, 12, 33, 722, 1, 34, 1, 34, 1, 35, 1, 35, 1, 36, 1, 36, 1, 36, 3, 36, 732, 8, 36, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 3, 38, 739, 8, 38, 1, 39, 1, 39, 1, 39, 5, 39, 744, 8, 39, 10, 39, 12, 39, 747, 9, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 5, 39, 755, 8, 39, 10, 39, 12, 39, 758, 9, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 3, 39, 765, 8, 39, 1, 39, 3, 39, 768, 8, 39, 3, 39, 770, 8, 39, 1, 40, 4, 40, 773, 8, 40, 11, 40, 12, 40, 774, 1, 41, 4, 41, 778, 8, 41, 11, 41, 12, 41, 779, 1, 41, 1, 41, 5, 41, 784, 8, 41, 10, 41, 12, 41, 787, 9, 41, 1, 41, 1, 41, 4, 41, 791, 8, 41, 11, 41, 12, 41, 792, 1, 41, 4, 41, 796, 8, 41, 11, 41, 12, 41, 797, 1, 41, 1, 41, 5, 41, 802, 8, 41, 10, 41, 12, 41, 805, 9, 41, 3, 41, 807, 8, 41, 1, 41, 1, 41, 1, 41, 1, 41, 4, 41, 813, 8, 41, 11, 41, 12, 41, 814, 1, 41, 1, 41, 3, 41, 819, 8, 41, 1, 42, 1, 42, 1, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 44, 1, 44, 1, 44, 1, 44, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 50, 1, 50, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 54, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 57, 1, 57, 1, 58, 1, 58, 1, 58, 1, 58, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 61, 1, 61, 1, 61, 1, 62, 1, 62, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 64, 1, 64, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 66, 1, 66, 1, 66, 1, 67, 1, 67, 1, 67, 1, 68, 1, 68, 1, 68, 1, 69, 1, 69, 1, 70, 1, 70, 1, 70, 1, 71, 1, 71, 1, 72, 1, 72, 1, 72, 1, 73, 1, 73, 1, 74, 1, 74, 1, 75, 1, 75, 1, 76, 1, 76, 1, 77, 1, 77, 1, 78, 1, 78, 1, 78, 1, 79, 1, 79, 1, 79, 1, 80, 1, 80, 1, 80, 1, 80, 1, 81, 1, 81, 1, 81, 3, 81, 953, 8, 81, 1, 81, 5, 81, 956, 8, 81, 10, 81, 12, 81, 959, 9, 81, 1, 81, 1, 81, 4, 81, 963, 8, 81, 11, 81, 12, 81, 964, 3, 81, 967, 8, 81, 1, 82, 1, 82, 1, 82, 1, 82, 1, 82, 1, 83, 1, 83, 1, 83, 1, 83, 1, 83, 1, 84, 1, 84, 5, 84, 981, 8, 84, 10, 84, 12, 84, 984, 9, 84, 1, 84, 1, 84, 3, 84, 988, 8, 84, 1, 84, 4, 84, 991, 8, 84, 11, 84, 12, 84, 992, 3, 84, 995, 8, 84, 1, 85, 1, 85, 4, 85, 999, 8, 85, 11, 85, 12, 85, 1000, 1, 85, 1, 85, 1, 86, 1, 86, 1, 87, 1, 87, 1, 87, 1, 87, 1, 88, 1, 88, 1, 88, 1, 88, 1, 89, 1, 89, 1, 89, 1, 89, 1, 90, 1, 90, 1, 90, 1, 90, 1, 90, 1, 91, 1, 91, 1, 91, 1, 91, 1, 91, 1, 92, 1, 92, 1, 92, 1, 92, 1, 93, 1, 93, 1, 93, 1, 93, 1, 94, 1, 94, 1, 94, 1, 94, 1, 95, 1, 95, 1, 95, 1, 95, 1, 95, 1, 96, 1, 96, 1, 96, 1, 96, 1, 97, 1, 97, 1, 97, 1, 97, 1, 98, 1, 98, 1, 98, 1, 98, 1, 99, 1, 99, 1, 99, 1, 99, 1, 100, 1, 100, 1, 100, 1, 100, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 102, 1, 102, 1, 102, 3, 102, 1078, 8, 102, 1, 103, 4, 103, 1081, 8, 103, 11, 103, 12, 103, 1082, 1, 104, 1, 104, 1, 104, 1, 104, 1, 105, 1, 105, 1, 105, 1, 105, 1, 106, 1, 106, 1, 106, 1, 106, 1, 107, 1, 107, 1, 107, 1, 107, 1, 108, 1, 108, 1, 108, 1, 108, 1, 109, 1, 109, 1, 109, 1, 109, 1, 109, 1, 110, 1, 110, 1, 110, 1, 110, 1, 111, 1, 111, 1, 111, 1, 111, 1, 112, 1, 112, 1, 112, 1, 112, 1, 112, 1, 113, 1, 113, 1, 113, 1, 113, 1, 113, 1, 114, 1, 114, 1, 114, 1, 114, 3, 114, 1132, 8, 114, 1, 115, 1, 115, 3, 115, 1136, 8, 115, 1, 115, 5, 115, 1139, 8, 115, 10, 115, 12, 115, 1142, 9, 115, 1, 115, 1, 115, 3, 115, 1146, 8, 115, 1, 115, 4, 115, 1149, 8, 115, 11, 115, 12, 115, 1150, 3, 115, 1153, 8, 115, 1, 116, 1, 116, 4, 116, 1157, 8, 116, 11, 116, 12, 116, 1158, 1, 117, 1, 117, 1, 117, 1, 117, 1, 118, 1, 118, 1, 118, 1, 118, 1, 119, 1, 119, 1, 119, 1, 119, 1, 120, 1, 120, 1, 120, 1, 120, 1, 120, 1, 121, 1, 121, 1, 121, 1, 121, 1, 122, 1, 122, 1, 122, 1, 122, 1, 123, 1, 123, 1, 123, 1, 123, 1, 124, 1, 124, 1, 124, 1, 124, 1, 124, 1, 125, 1, 125, 1, 125, 1, 125, 1, 125, 1, 126, 1, 126, 1, 126, 1, 127, 1, 127, 1, 127, 1, 127, 1, 128, 1, 128, 1, 128, 1, 128, 1, 129, 1, 129, 1, 129, 1, 129, 1, 130, 1, 130, 1, 130, 1, 130, 1, 131, 1, 131, 1, 131, 1, 131, 1, 131, 1, 132, 1, 132, 1, 132, 1, 132, 1, 132, 1, 133, 1, 133, 1, 133, 1, 133, 1, 133, 1, 134, 1, 134, 1, 134, 1, 134, 1, 134, 1, 134, 1, 134, 1, 135, 1, 135, 1, 136, 4, 136, 1244, 8, 136, 11, 136, 12, 136, 1245, 1, 136, 1, 136, 3, 136, 1250, 8, 136, 1, 136, 4, 136, 1253, 8, 136, 11, 136, 12, 136, 1254, 1, 137, 1, 137, 1, 137, 1, 137, 1, 138, 1, 138, 1, 138, 1, 138, 1, 139, 1, 139, 1, 139, 1, 139, 1, 140, 1, 140, 1, 140, 1, 140, 1, 141, 1, 141, 1, 141, 1, 141, 1, 141, 1, 141, 1, 142, 1, 142, 1, 142, 1, 142, 1, 143, 1, 143, 1, 143, 1, 143, 1, 144, 1, 144, 1, 144, 1, 144, 1, 145, 1, 145, 1, 145, 1, 145, 1, 146, 1, 146, 1, 146, 1, 146, 1, 147, 1, 147, 1, 147, 1, 147, 1, 148, 1, 148, 1, 148, 1, 148, 1, 148, 1, 149, 1, 149, 1, 149, 1, 149, 1, 149, 1, 150, 1, 150, 1, 150, 1, 150, 1, 151, 1, 151, 1, 151, 1, 151, 1, 152, 1, 152, 1, 152, 1, 152, 1, 153, 1, 153, 1, 153, 1, 153, 1, 153, 1, 154, 1, 154, 1, 154, 1, 154, 1, 155, 1, 155, 1, 155, 1, 155, 1, 155, 1, 156, 1, 156, 1, 156, 1, 156, 1, 156, 1, 157, 1, 157, 1, 157, 1, 157, 1, 158, 1, 158, 1, 158, 1, 158, 1, 159, 1, 159, 1, 159, 1, 159, 1, 160, 1, 160, 1, 160, 1, 160, 1, 161, 1, 161, 1, 161, 1, 161, 1, 162, 1, 162, 1, 162, 1, 162, 1, 162, 1, 163, 1, 163, 1, 163, 1, 163, 1, 163, 1, 164, 1, 164, 1, 164, 1, 164, 1, 165, 1, 165, 1, 165, 1, 165, 1, 166, 1, 166, 1, 166, 1, 166, 1, 167, 1, 167, 1, 167, 1, 167, 1, 167, 1, 168, 1, 168, 1, 168, 1, 168, 1, 169, 1, 169, 1, 169, 1, 169, 1, 169, 4, 169, 1400, 8, 169, 11, 169, 12, 169, 1401, 1, 170, 1, 170, 1, 170, 1, 170, 1, 171, 1, 171, 1, 171, 1, 171, 1, 172, 1, 172, 1, 172, 1, 172, 1, 173, 1, 173, 1, 173, 1, 173, 1, 173, 1, 174, 1, 174, 1, 174, 1, 174, 1, 175, 1, 175, 1, 175, 1, 175, 1, 176, 1, 176, 1, 176, 1, 176, 1, 177, 1, 177, 1, 177, 1, 177, 1, 177, 1, 178, 1, 178, 1, 178, 1, 178, 1, 179, 1, 179, 1, 179, 1, 179, 1, 180, 1, 180, 1, 180, 1, 180, 1, 181, 1, 181, 1, 181, 1, 181, 1, 182, 1, 182, 1, 182, 1, 182, 1, 183, 1, 183, 1, 183, 1, 183, 1, 183, 1, 183, 1, 184, 1, 184, 1, 184, 1, 184, 1, 185, 1, 185, 1, 185, 1, 185, 1, 186, 1, 186, 1, 186, 1, 186, 1, 187, 1, 187, 1, 187, 1, 187, 1, 188, 1, 188, 1, 188, 1, 188, 1, 189, 1, 189, 1, 189, 1, 189, 1, 190, 1, 190, 1, 190, 1, 190, 1, 190, 1, 191, 1, 191, 1, 191, 1, 191, 1, 192, 1, 192, 1, 192, 1, 192, 1, 193, 1, 193, 1, 193, 1, 193, 1, 193, 1, 193, 1, 194, 1, 194, 1, 194, 1, 194, 1, 194, 1, 194, 1, 194, 1, 194, 1, 194, 1, 195, 1, 195, 1, 195, 1, 195, 1, 196, 1, 196, 1, 196, 1, 196, 1, 197, 1, 197, 1, 197, 1, 197, 1, 198, 1, 198, 1, 198, 1, 198, 1, 199, 1, 199, 1, 199, 1, 199, 1, 200, 1, 200, 1, 200, 1, 200, 1, 200, 1, 201, 1, 201, 1, 201, 1, 201, 1, 201, 1, 201, 1, 202, 1, 202, 1, 202, 1, 202, 1, 202, 1, 202, 1, 203, 1, 203, 1, 203, 1, 203, 1, 204, 1, 204, 1, 204, 1, 204, 1, 205, 1, 205, 1, 205, 1, 205, 1, 206, 1, 206, 1, 206, 1, 206, 1, 206, 1, 206, 1, 207, 1, 207, 1, 207, 1, 207, 1, 207, 1, 207, 1, 208, 1, 208, 1, 208, 1, 208, 1, 209, 1, 209, 1, 209, 1, 209, 1, 210, 1, 210, 1, 210, 1, 210, 1, 211, 1, 211, 1, 211, 1, 211, 1, 211, 1, 211, 1, 212, 1, 212, 1, 212, 1, 212, 1, 212, 1, 212, 1, 213, 1, 213, 1, 213, 1, 213, 1, 213, 1, 213, 1, 214, 1, 214, 1, 214, 1, 214, 1, 214, 2, 687, 756, 0, 215, 16, 1, 18, 2, 20, 3, 22, 4, 24, 5, 26, 6, 28, 7, 30, 8, 32, 9, 34, 10, 36, 11, 38, 12, 40, 13, 42, 14, 44, 15, 46, 16, 48, 17, 50, 18, 52, 19, 54, 20, 56, 21, 58, 22, 60, 23, 62, 24, 64, 25, 66, 26, 68, 27, 70, 28, 72, 29, 74, 0, 76, 0, 78, 0, 80, 0, 82, 0, 84, 0, 86, 0, 88, 0, 90, 0, 92, 0, 94, 30, 96, 31, 98, 32, 100, 33, 102, 34, 104, 35, 106, 36, 108, 37, 110, 38, 112, 39, 114, 40, 116, 41, 118, 42, 120, 43, 122, 44, 124, 45, 126, 46, 128, 47, 130, 48, 132, 49, 134, 50, 136, 51, 138, 52, 140, 53, 142, 54, 144, 55, 146, 56, 148, 57, 150, 58, 152, 59, 154, 60, 156, 61, 158, 62, 160, 63, 162, 64, 164, 65, 166, 66, 168, 67, 170, 68, 172, 69, 174, 70, 176, 0, 178, 71, 180, 72, 182, 73, 184, 74, 186, 0, 188, 75, 190, 76, 192, 77, 194, 78, 196, 0, 198, 0, 200, 79, 202, 80, 204, 81, 206, 0, 208, 0, 210, 0, 212, 0, 214, 0, 216, 0, 218, 82, 220, 0, 222, 83, 224, 0, 226, 0, 228, 84, 230, 85, 232, 86, 234, 0, 236, 0, 238, 0, 240, 0, 242, 0, 244, 0, 246, 0, 248, 87, 250, 88, 252, 89, 254, 90, 256, 0, 258, 0, 260, 0, 262, 0, 264, 0, 266, 0, 268, 91, 270, 0, 272, 92, 274, 93, 276, 94, 278, 0, 280, 0, 282, 95, 284, 96, 286, 0, 288, 97, 290, 0, 292, 98, 294, 99, 296, 100, 298, 0, 300, 0, 302, 0, 304, 0, 306, 0, 308, 0, 310, 0, 312, 0, 314, 0, 316, 101, 318, 102, 320, 103, 322, 0, 324, 0, 326, 0, 328, 0, 330, 0, 332, 0, 334, 104, 336, 105, 338, 106, 340, 0, 342, 107, 344, 108, 346, 109, 348, 110, 350, 0, 352, 0, 354, 111, 356, 112, 358, 113, 360, 114, 362, 0, 364, 0, 366, 0, 368, 0, 370, 0, 372, 0, 374, 0, 376, 115, 378, 116, 380, 117, 382, 0, 384, 0, 386, 0, 388, 0, 390, 118, 392, 119, 394, 120, 396, 0, 398, 0, 400, 0, 402, 0, 404, 121, 406, 0, 408, 0, 410, 122, 412, 123, 414, 124, 416, 0, 418, 0, 420, 0, 422, 125, 424, 126, 426, 127, 428, 0, 430, 0, 432, 128, 434, 129, 436, 130, 438, 0, 440, 0, 442, 0, 444, 0, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 36, 2, 0, 68, 68, 100, 100, 2, 0, 73, 73, 105, 105, 2, 0, 83, 83, 115, 115, 2, 0, 69, 69, 101, 101, 2, 0, 67, 67, 99, 99, 2, 0, 84, 84, 116, 116, 2, 0, 82, 82, 114, 114, 2, 0, 79, 79, 111, 111, 2, 0, 80, 80, 112, 112, 2, 0, 78, 78, 110, 110, 2, 0, 72, 72, 104, 104, 2, 0, 86, 86, 118, 118, 2, 0, 65, 65, 97, 97, 2, 0, 76, 76, 108, 108, 2, 0, 88, 88, 120, 120, 2, 0, 70, 70, 102, 102, 2, 0, 77, 77, 109, 109, 2, 0, 71, 71, 103, 103, 2, 0, 75, 75, 107, 107, 2, 0, 87, 87, 119, 119, 2, 0, 85, 85, 117, 117, 2, 0, 74, 74, 106, 106, 6, 0, 9, 10, 13, 13, 32, 32, 47, 47, 91, 91, 93, 93, 2, 0, 10, 10, 13, 13, 3, 0, 9, 10, 13, 13, 32, 32, 1, 0, 48, 57, 2, 0, 65, 90, 97, 122, 8, 0, 34, 34, 78, 78, 82, 82, 84, 84, 92, 92, 110, 110, 114, 114, 116, 116, 4, 0, 10, 10, 13, 13, 34, 34, 92, 92, 2, 0, 43, 43, 45, 45, 1, 0, 96, 96, 2, 0, 66, 66, 98, 98, 2, 0, 89, 89, 121, 121, 11, 0, 9, 10, 13, 13, 32, 32, 34, 34, 44, 44, 47, 47, 58, 58, 61, 61, 91, 91, 93, 93, 124, 124, 2, 0, 42, 42, 47, 47, 11, 0, 9, 10, 13, 13, 32, 32, 34, 35, 44, 44, 47, 47, 58, 58, 60, 60, 62, 63, 92, 92, 124, 124, 1638, 0, 16, 1, 0, 0, 0, 0, 18, 1, 0, 0, 0, 0, 20, 1, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 24, 1, 0, 0, 0, 0, 26, 1, 0, 0, 0, 0, 28, 1, 0, 0, 0, 0, 30, 1, 0, 0, 0, 0, 32, 1, 0, 0, 0, 0, 34, 1, 0, 0, 0, 0, 36, 1, 0, 0, 0, 0, 38, 1, 0, 0, 0, 0, 40, 1, 0, 0, 0, 0, 42, 1, 0, 0, 0, 0, 44, 1, 0, 0, 0, 0, 46, 1, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 50, 1, 0, 0, 0, 0, 52, 1, 0, 0, 0, 0, 54, 1, 0, 0, 0, 0, 56, 1, 0, 0, 0, 0, 58, 1, 0, 0, 0, 0, 60, 1, 0, 0, 0, 0, 62, 1, 0, 0, 0, 0, 64, 1, 0, 0, 0, 0, 66, 1, 0, 0, 0, 0, 68, 1, 0, 0, 0, 0, 70, 1, 0, 0, 0, 1, 72, 1, 0, 0, 0, 1, 94, 1, 0, 0, 0, 1, 96, 1, 0, 0, 0, 1, 98, 1, 0, 0, 0, 1, 100, 1, 0, 0, 0, 1, 102, 1, 0, 0, 0, 1, 104, 1, 0, 0, 0, 1, 106, 1, 0, 0, 0, 1, 108, 1, 0, 0, 0, 1, 110, 1, 0, 0, 0, 1, 112, 1, 0, 0, 0, 1, 114, 1, 0, 0, 0, 1, 116, 1, 0, 0, 0, 1, 118, 1, 0, 0, 0, 1, 120, 1, 0, 0, 0, 1, 122, 1, 0, 0, 0, 1, 124, 1, 0, 0, 0, 1, 126, 1, 0, 0, 0, 1, 128, 1, 0, 0, 0, 1, 130, 1, 0, 0, 0, 1, 132, 1, 0, 0, 0, 1, 134, 1, 0, 0, 0, 1, 136, 1, 0, 0, 0, 1, 138, 1, 0, 0, 0, 1, 140, 1, 0, 0, 0, 1, 142, 1, 0, 0, 0, 1, 144, 1, 0, 0, 0, 1, 146, 1, 0, 0, 0, 1, 148, 1, 0, 0, 0, 1, 150, 1, 0, 0, 0, 1, 152, 1, 0, 0, 0, 1, 154, 1, 0, 0, 0, 1, 156, 1, 0, 0, 0, 1, 158, 1, 0, 0, 0, 1, 160, 1, 0, 0, 0, 1, 162, 1, 0, 0, 0, 1, 164, 1, 0, 0, 0, 1, 166, 1, 0, 0, 0, 1, 168, 1, 0, 0, 0, 1, 170, 1, 0, 0, 0, 1, 172, 1, 0, 0, 0, 1, 174, 1, 0, 0, 0, 1, 176, 1, 0, 0, 0, 1, 178, 1, 0, 0, 0, 1, 180, 1, 0, 0, 0, 1, 182, 1, 0, 0, 0, 1, 184, 1, 0, 0, 0, 1, 188, 1, 0, 0, 0, 1, 190, 1, 0, 0, 0, 1, 192, 1, 0, 0, 0, 1, 194, 1, 0, 0, 0, 2, 196, 1, 0, 0, 0, 2, 198, 1, 0, 0, 0, 2, 200, 1, 0, 0, 0, 2, 202, 1, 0, 0, 0, 2, 204, 1, 0, 0, 0, 3, 206, 1, 0, 0, 0, 3, 208, 1, 0, 0, 0, 3, 210, 1, 0, 0, 0, 3, 212, 1, 0, 0, 0, 3, 214, 1, 0, 0, 0, 3, 216, 1, 0, 0, 0, 3, 218, 1, 0, 0, 0, 3, 222, 1, 0, 0, 0, 3, 224, 1, 0, 0, 0, 3, 226, 1, 0, 0, 0, 3, 228, 1, 0, 0, 0, 3, 230, 1, 0, 0, 0, 3, 232, 1, 0, 0, 0, 4, 234, 1, 0, 0, 0, 4, 236, 1, 0, 0, 0, 4, 238, 1, 0, 0, 0, 4, 240, 1, 0, 0, 0, 4, 242, 1, 0, 0, 0, 4, 248, 1, 0, 0, 0, 4, 250, 1, 0, 0, 0, 4, 252, 1, 0, 0, 0, 4, 254, 1, 0, 0, 0, 5, 256, 1, 0, 0, 0, 5, 258, 1, 0, 0, 0, 5, 260, 1, 0, 0, 0, 5, 262, 1, 0, 0, 0, 5, 264, 1, 0, 0, 0, 5, 266, 1, 0, 0, 0, 5, 268, 1, 0, 0, 0, 5, 270, 1, 0, 0, 0, 5, 272, 1, 0, 0, 0, 5, 274, 1, 0, 0, 0, 5, 276, 1, 0, 0, 0, 6, 278, 1, 0, 0, 0, 6, 280, 1, 0, 0, 0, 6, 282, 1, 0, 0, 0, 6, 284, 1, 0, 0, 0, 6, 288, 1, 0, 0, 0, 6, 290, 1, 0, 0, 0, 6, 292, 1, 0, 0, 0, 6, 294, 1, 0, 0, 0, 6, 296, 1, 0, 0, 0, 7, 298, 1, 0, 0, 0, 7, 300, 1, 0, 0, 0, 7, 302, 1, 0, 0, 0, 7, 304, 1, 0, 0, 0, 7, 306, 1, 0, 0, 0, 7, 308, 1, 0, 0, 0, 7, 310, 1, 0, 0, 0, 7, 312, 1, 0, 0, 0, 7, 314, 1, 0, 0, 0, 7, 316, 1, 0, 0, 0, 7, 318, 1, 0, 0, 0, 7, 320, 1, 0, 0, 0, 8, 322, 1, 0, 0, 0, 8, 324, 1, 0, 0, 0, 8, 326, 1, 0, 0, 0, 8, 328, 1, 0, 0, 0, 8, 330, 1, 0, 0, 0, 8, 332, 1, 0, 0, 0, 8, 334, 1, 0, 0, 0, 8, 336, 1, 0, 0, 0, 8, 338, 1, 0, 0, 0, 9, 340, 1, 0, 0, 0, 9, 342, 1, 0, 0, 0, 9, 344, 1, 0, 0, 0, 9, 346, 1, 0, 0, 0, 9, 348, 1, 0, 0, 0, 10, 350, 1, 0, 0, 0, 10, 352, 1, 0, 0, 0, 10, 354, 1, 0, 0, 0, 10, 356, 1, 0, 0, 0, 10, 358, 1, 0, 0, 0, 10, 360, 1, 0, 0, 0, 11, 362, 1, 0, 0, 0, 11, 364, 1, 0, 0, 0, 11, 366, 1, 0, 0, 0, 11, 368, 1, 0, 0, 0, 11, 370, 1, 0, 0, 0, 11, 372, 1, 0, 0, 0, 11, 374, 1, 0, 0, 0, 11, 376, 1, 0, 0, 0, 11, 378, 1, 0, 0, 0, 11, 380, 1, 0, 0, 0, 12, 382, 1, 0, 0, 0, 12, 384, 1, 0, 0, 0, 12, 386, 1, 0, 0, 0, 12, 388, 1, 0, 0, 0, 12, 390, 1, 0, 0, 0, 12, 392, 1, 0, 0, 0, 12, 394, 1, 0, 0, 0, 13, 396, 1, 0, 0, 0, 13, 398, 1, 0, 0, 0, 13, 400, 1, 0, 0, 0, 13, 402, 1, 0, 0, 0, 13, 404, 1, 0, 0, 0, 13, 406, 1, 0, 0, 0, 13, 408, 1, 0, 0, 0, 13, 410, 1, 0, 0, 0, 13, 412, 1, 0, 0, 0, 13, 414, 1, 0, 0, 0, 14, 416, 1, 0, 0, 0, 14, 418, 1, 0, 0, 0, 14, 420, 1, 0, 0, 0, 14, 422, 1, 0, 0, 0, 14, 424, 1, 0, 0, 0, 14, 426, 1, 0, 0, 0, 15, 428, 1, 0, 0, 0, 15, 430, 1, 0, 0, 0, 15, 432, 1, 0, 0, 0, 15, 434, 1, 0, 0, 0, 15, 436, 1, 0, 0, 0, 15, 438, 1, 0, 0, 0, 15, 440, 1, 0, 0, 0, 15, 442, 1, 0, 0, 0, 15, 444, 1, 0, 0, 0, 16, 446, 1, 0, 0, 0, 18, 456, 1, 0, 0, 0, 20, 463, 1, 0, 0, 0, 22, 472, 1, 0, 0, 0, 24, 479, 1, 0, 0, 0, 26, 489, 1, 0, 0, 0, 28, 496, 1, 0, 0, 0, 30, 503, 1, 0, 0, 0, 32, 510, 1, 0, 0, 0, 34, 518, 1, 0, 0, 0, 36, 530, 1, 0, 0, 0, 38, 539, 1, 0, 0, 0, 40, 545, 1, 0, 0, 0, 42, 552, 1, 0, 0, 0, 44, 559, 1, 0, 0, 0, 46, 567, 1, 0, 0, 0, 48, 575, 1, 0, 0, 0, 50, 590, 1, 0, 0, 0, 52, 602, 1, 0, 0, 0, 54, 613, 1, 0, 0, 0, 56, 621, 1, 0, 0, 0, 58, 629, 1, 0, 0, 0, 60, 637, 1, 0, 0, 0, 62, 646, 1, 0, 0, 0, 64, 657, 1, 0, 0, 0, 66, 663, 1, 0, 0, 0, 68, 680, 1, 0, 0, 0, 70, 696, 1, 0, 0, 0, 72, 702, 1, 0, 0, 0, 74, 706, 1, 0, 0, 0, 76, 708, 1, 0, 0, 0, 78, 710, 1, 0, 0, 0, 80, 713, 1, 0, 0, 0, 82, 715, 1, 0, 0, 0, 84, 724, 1, 0, 0, 0, 86, 726, 1, 0, 0, 0, 88, 731, 1, 0, 0, 0, 90, 733, 1, 0, 0, 0, 92, 738, 1, 0, 0, 0, 94, 769, 1, 0, 0, 0, 96, 772, 1, 0, 0, 0, 98, 818, 1, 0, 0, 0, 100, 820, 1, 0, 0, 0, 102, 823, 1, 0, 0, 0, 104, 827, 1, 0, 0, 0, 106, 831, 1, 0, 0, 0, 108, 833, 1, 0, 0, 0, 110, 836, 1, 0, 0, 0, 112, 838, 1, 0, 0, 0, 114, 840, 1, 0, 0, 0, 116, 845, 1, 0, 0, 0, 118, 847, 1, 0, 0, 0, 120, 853, 1, 0, 0, 0, 122, 859, 1, 0, 0, 0, 124, 862, 1, 0, 0, 0, 126, 865, 1, 0, 0, 0, 128, 870, 1, 0, 0, 0, 130, 875, 1, 0, 0, 0, 132, 877, 1, 0, 0, 0, 134, 881, 1, 0, 0, 0, 136, 886, 1, 0, 0, 0, 138, 892, 1, 0, 0, 0, 140, 895, 1, 0, 0, 0, 142, 897, 1, 0, 0, 0, 144, 903, 1, 0, 0, 0, 146, 905, 1, 0, 0, 0, 148, 910, 1, 0, 0, 0, 150, 913, 1, 0, 0, 0, 152, 916, 1, 0, 0, 0, 154, 919, 1, 0, 0, 0, 156, 921, 1, 0, 0, 0, 158, 924, 1, 0, 0, 0, 160, 926, 1, 0, 0, 0, 162, 929, 1, 0, 0, 0, 164, 931, 1, 0, 0, 0, 166, 933, 1, 0, 0, 0, 168, 935, 1, 0, 0, 0, 170, 937, 1, 0, 0, 0, 172, 939, 1, 0, 0, 0, 174, 942, 1, 0, 0, 0, 176, 945, 1, 0, 0, 0, 178, 966, 1, 0, 0, 0, 180, 968, 1, 0, 0, 0, 182, 973, 1, 0, 0, 0, 184, 994, 1, 0, 0, 0, 186, 996, 1, 0, 0, 0, 188, 1004, 1, 0, 0, 0, 190, 1006, 1, 0, 0, 0, 192, 1010, 1, 0, 0, 0, 194, 1014, 1, 0, 0, 0, 196, 1018, 1, 0, 0, 0, 198, 1023, 1, 0, 0, 0, 200, 1028, 1, 0, 0, 0, 202, 1032, 1, 0, 0, 0, 204, 1036, 1, 0, 0, 0, 206, 1040, 1, 0, 0, 0, 208, 1045, 1, 0, 0, 0, 210, 1049, 1, 0, 0, 0, 212, 1053, 1, 0, 0, 0, 214, 1057, 1, 0, 0, 0, 216, 1061, 1, 0, 0, 0, 218, 1065, 1, 0, 0, 0, 220, 1077, 1, 0, 0, 0, 222, 1080, 1, 0, 0, 0, 224, 1084, 1, 0, 0, 0, 226, 1088, 1, 0, 0, 0, 228, 1092, 1, 0, 0, 0, 230, 1096, 1, 0, 0, 0, 232, 1100, 1, 0, 0, 0, 234, 1104, 1, 0, 0, 0, 236, 1109, 1, 0, 0, 0, 238, 1113, 1, 0, 0, 0, 240, 1117, 1, 0, 0, 0, 242, 1122, 1, 0, 0, 0, 244, 1131, 1, 0, 0, 0, 246, 1152, 1, 0, 0, 0, 248, 1156, 1, 0, 0, 0, 250, 1160, 1, 0, 0, 0, 252, 1164, 1, 0, 0, 0, 254, 1168, 1, 0, 0, 0, 256, 1172, 1, 0, 0, 0, 258, 1177, 1, 0, 0, 0, 260, 1181, 1, 0, 0, 0, 262, 1185, 1, 0, 0, 0, 264, 1189, 1, 0, 0, 0, 266, 1194, 1, 0, 0, 0, 268, 1199, 1, 0, 0, 0, 270, 1202, 1, 0, 0, 0, 272, 1206, 1, 0, 0, 0, 274, 1210, 1, 0, 0, 0, 276, 1214, 1, 0, 0, 0, 278, 1218, 1, 0, 0, 0, 280, 1223, 1, 0, 0, 0, 282, 1228, 1, 0, 0, 0, 284, 1233, 1, 0, 0, 0, 286, 1240, 1, 0, 0, 0, 288, 1249, 1, 0, 0, 0, 290, 1256, 1, 0, 0, 0, 292, 1260, 1, 0, 0, 0, 294, 1264, 1, 0, 0, 0, 296, 1268, 1, 0, 0, 0, 298, 1272, 1, 0, 0, 0, 300, 1278, 1, 0, 0, 0, 302, 1282, 1, 0, 0, 0, 304, 1286, 1, 0, 0, 0, 306, 1290, 1, 0, 0, 0, 308, 1294, 1, 0, 0, 0, 310, 1298, 1, 0, 0, 0, 312, 1302, 1, 0, 0, 0, 314, 1307, 1, 0, 0, 0, 316, 1312, 1, 0, 0, 0, 318, 1316, 1, 0, 0, 0, 320, 1320, 1, 0, 0, 0, 322, 1324, 1, 0, 0, 0, 324, 1329, 1, 0, 0, 0, 326, 1333, 1, 0, 0, 0, 328, 1338, 1, 0, 0, 0, 330, 1343, 1, 0, 0, 0, 332, 1347, 1, 0, 0, 0, 334, 1351, 1, 0, 0, 0, 336, 1355, 1, 0, 0, 0, 338, 1359, 1, 0, 0, 0, 340, 1363, 1, 0, 0, 0, 342, 1368, 1, 0, 0, 0, 344, 1373, 1, 0, 0, 0, 346, 1377, 1, 0, 0, 0, 348, 1381, 1, 0, 0, 0, 350, 1385, 1, 0, 0, 0, 352, 1390, 1, 0, 0, 0, 354, 1399, 1, 0, 0, 0, 356, 1403, 1, 0, 0, 0, 358, 1407, 1, 0, 0, 0, 360, 1411, 1, 0, 0, 0, 362, 1415, 1, 0, 0, 0, 364, 1420, 1, 0, 0, 0, 366, 1424, 1, 0, 0, 0, 368, 1428, 1, 0, 0, 0, 370, 1432, 1, 0, 0, 0, 372, 1437, 1, 0, 0, 0, 374, 1441, 1, 0, 0, 0, 376, 1445, 1, 0, 0, 0, 378, 1449, 1, 0, 0, 0, 380, 1453, 1, 0, 0, 0, 382, 1457, 1, 0, 0, 0, 384, 1463, 1, 0, 0, 0, 386, 1467, 1, 0, 0, 0, 388, 1471, 1, 0, 0, 0, 390, 1475, 1, 0, 0, 0, 392, 1479, 1, 0, 0, 0, 394, 1483, 1, 0, 0, 0, 396, 1487, 1, 0, 0, 0, 398, 1492, 1, 0, 0, 0, 400, 1496, 1, 0, 0, 0, 402, 1500, 1, 0, 0, 0, 404, 1506, 1, 0, 0, 0, 406, 1515, 1, 0, 0, 0, 408, 1519, 1, 0, 0, 0, 410, 1523, 1, 0, 0, 0, 412, 1527, 1, 0, 0, 0, 414, 1531, 1, 0, 0, 0, 416, 1535, 1, 0, 0, 0, 418, 1540, 1, 0, 0, 0, 420, 1546, 1, 0, 0, 0, 422, 1552, 1, 0, 0, 0, 424, 1556, 1, 0, 0, 0, 426, 1560, 1, 0, 0, 0, 428, 1564, 1, 0, 0, 0, 430, 1570, 1, 0, 0, 0, 432, 1576, 1, 0, 0, 0, 434, 1580, 1, 0, 0, 0, 436, 1584, 1, 0, 0, 0, 438, 1588, 1, 0, 0, 0, 440, 1594, 1, 0, 0, 0, 442, 1600, 1, 0, 0, 0, 444, 1606, 1, 0, 0, 0, 446, 447, 7, 0, 0, 0, 447, 448, 7, 1, 0, 0, 448, 449, 7, 2, 0, 0, 449, 450, 7, 2, 0, 0, 450, 451, 7, 3, 0, 0, 451, 452, 7, 4, 0, 0, 452, 453, 7, 5, 0, 0, 453, 454, 1, 0, 0, 0, 454, 455, 6, 0, 0, 0, 455, 17, 1, 0, 0, 0, 456, 457, 7, 0, 0, 0, 457, 458, 7, 6, 0, 0, 458, 459, 7, 7, 0, 0, 459, 460, 7, 8, 0, 0, 460, 461, 1, 0, 0, 0, 461, 462, 6, 1, 1, 0, 462, 19, 1, 0, 0, 0, 463, 464, 7, 3, 0, 0, 464, 465, 7, 9, 0, 0, 465, 466, 7, 6, 0, 0, 466, 467, 7, 1, 0, 0, 467, 468, 7, 4, 0, 0, 468, 469, 7, 10, 0, 0, 469, 470, 1, 0, 0, 0, 470, 471, 6, 2, 2, 0, 471, 21, 1, 0, 0, 0, 472, 473, 7, 3, 0, 0, 473, 474, 7, 11, 0, 0, 474, 475, 7, 12, 0, 0, 475, 476, 7, 13, 0, 0, 476, 477, 1, 0, 0, 0, 477, 478, 6, 3, 0, 0, 478, 23, 1, 0, 0, 0, 479, 480, 7, 3, 0, 0, 480, 481, 7, 14, 0, 0, 481, 482, 7, 8, 0, 0, 482, 483, 7, 13, 0, 0, 483, 484, 7, 12, 0, 0, 484, 485, 7, 1, 0, 0, 485, 486, 7, 9, 0, 0, 486, 487, 1, 0, 0, 0, 487, 488, 6, 4, 3, 0, 488, 25, 1, 0, 0, 0, 489, 490, 7, 15, 0, 0, 490, 491, 7, 6, 0, 0, 491, 492, 7, 7, 0, 0, 492, 493, 7, 16, 0, 0, 493, 494, 1, 0, 0, 0, 494, 495, 6, 5, 4, 0, 495, 27, 1, 0, 0, 0, 496, 497, 7, 17, 0, 0, 497, 498, 7, 6, 0, 0, 498, 499, 7, 7, 0, 0, 499, 500, 7, 18, 0, 0, 500, 501, 1, 0, 0, 0, 501, 502, 6, 6, 0, 0, 502, 29, 1, 0, 0, 0, 503, 504, 7, 18, 0, 0, 504, 505, 7, 3, 0, 0, 505, 506, 7, 3, 0, 0, 506, 507, 7, 8, 0, 0, 507, 508, 1, 0, 0, 0, 508, 509, 6, 7, 1, 0, 509, 31, 1, 0, 0, 0, 510, 511, 7, 13, 0, 0, 511, 512, 7, 1, 0, 0, 512, 513, 7, 16, 0, 0, 513, 514, 7, 1, 0, 0, 514, 515, 7, 5, 0, 0, 515, 516, 1, 0, 0, 0, 516, 517, 6, 8, 0, 0, 517, 33, 1, 0, 0, 0, 518, 519, 7, 16, 0, 0, 519, 520, 7, 11, 0, 0, 520, 521, 5, 95, 0, 0, 521, 522, 7, 3, 0, 0, 522, 523, 7, 14, 0, 0, 523, 524, 7, 8, 0, 0, 524, 525, 7, 12, 0, 0, 525, 526, 7, 9, 0, 0, 526, 527, 7, 0, 0, 0, 527, 528, 1, 0, 0, 0, 528, 529, 6, 9, 5, 0, 529, 35, 1, 0, 0, 0, 530, 531, 7, 6, 0, 0, 531, 532, 7, 3, 0, 0, 532, 533, 7, 9, 0, 0, 533, 534, 7, 12, 0, 0, 534, 535, 7, 16, 0, 0, 535, 536, 7, 3, 0, 0, 536, 537, 1, 0, 0, 0, 537, 538, 6, 10, 6, 0, 538, 37, 1, 0, 0, 0, 539, 540, 7, 6, 0, 0, 540, 541, 7, 7, 0, 0, 541, 542, 7, 19, 0, 0, 542, 543, 1, 0, 0, 0, 543, 544, 6, 11, 0, 0, 544, 39, 1, 0, 0, 0, 545, 546, 7, 2, 0, 0, 546, 547, 7, 10, 0, 0, 547, 548, 7, 7, 0, 0, 548, 549, 7, 19, 0, 0, 549, 550, 1, 0, 0, 0, 550, 551, 6, 12, 7, 0, 551, 41, 1, 0, 0, 0, 552, 553, 7, 2, 0, 0, 553, 554, 7, 7, 0, 0, 554, 555, 7, 6, 0, 0, 555, 556, 7, 5, 0, 0, 556, 557, 1, 0, 0, 0, 557, 558, 6, 13, 0, 0, 558, 43, 1, 0, 0, 0, 559, 560, 7, 2, 0, 0, 560, 561, 7, 5, 0, 0, 561, 562, 7, 12, 0, 0, 562, 563, 7, 5, 0, 0, 563, 564, 7, 2, 0, 0, 564, 565, 1, 0, 0, 0, 565, 566, 6, 14, 0, 0, 566, 45, 1, 0, 0, 0, 567, 568, 7, 19, 0, 0, 568, 569, 7, 10, 0, 0, 569, 570, 7, 3, 0, 0, 570, 571, 7, 6, 0, 0, 571, 572, 7, 3, 0, 0, 572, 573, 1, 0, 0, 0, 573, 574, 6, 15, 0, 0, 574, 47, 1, 0, 0, 0, 575, 576, 4, 16, 0, 0, 576, 577, 7, 1, 0, 0, 577, 578, 7, 9, 0, 0, 578, 579, 7, 13, 0, 0, 579, 580, 7, 1, 0, 0, 580, 581, 7, 9, 0, 0, 581, 582, 7, 3, 0, 0, 582, 583, 7, 2, 0, 0, 583, 584, 7, 5, 0, 0, 584, 585, 7, 12, 0, 0, 585, 586, 7, 5, 0, 0, 586, 587, 7, 2, 0, 0, 587, 588, 1, 0, 0, 0, 588, 589, 6, 16, 0, 0, 589, 49, 1, 0, 0, 0, 590, 591, 4, 17, 1, 0, 591, 592, 7, 13, 0, 0, 592, 593, 7, 7, 0, 0, 593, 594, 7, 7, 0, 0, 594, 595, 7, 18, 0, 0, 595, 596, 7, 20, 0, 0, 596, 597, 7, 8, 0, 0, 597, 598, 5, 95, 0, 0, 598, 599, 5, 128020, 0, 0, 599, 600, 1, 0, 0, 0, 600, 601, 6, 17, 8, 0, 601, 51, 1, 0, 0, 0, 602, 603, 4, 18, 2, 0, 603, 604, 7, 16, 0, 0, 604, 605, 7, 3, 0, 0, 605, 606, 7, 5, 0, 0, 606, 607, 7, 6, 0, 0, 607, 608, 7, 1, 0, 0, 608, 609, 7, 4, 0, 0, 609, 610, 7, 2, 0, 0, 610, 611, 1, 0, 0, 0, 611, 612, 6, 18, 9, 0, 612, 53, 1, 0, 0, 0, 613, 614, 4, 19, 3, 0, 614, 615, 7, 21, 0, 0, 615, 616, 7, 7, 0, 0, 616, 617, 7, 1, 0, 0, 617, 618, 7, 9, 0, 0, 618, 619, 1, 0, 0, 0, 619, 620, 6, 19, 10, 0, 620, 55, 1, 0, 0, 0, 621, 622, 4, 20, 4, 0, 622, 623, 7, 15, 0, 0, 623, 624, 7, 20, 0, 0, 624, 625, 7, 13, 0, 0, 625, 626, 7, 13, 0, 0, 626, 627, 1, 0, 0, 0, 627, 628, 6, 20, 10, 0, 628, 57, 1, 0, 0, 0, 629, 630, 4, 21, 5, 0, 630, 631, 7, 13, 0, 0, 631, 632, 7, 3, 0, 0, 632, 633, 7, 15, 0, 0, 633, 634, 7, 5, 0, 0, 634, 635, 1, 0, 0, 0, 635, 636, 6, 21, 10, 0, 636, 59, 1, 0, 0, 0, 637, 638, 4, 22, 6, 0, 638, 639, 7, 6, 0, 0, 639, 640, 7, 1, 0, 0, 640, 641, 7, 17, 0, 0, 641, 642, 7, 10, 0, 0, 642, 643, 7, 5, 0, 0, 643, 644, 1, 0, 0, 0, 644, 645, 6, 22, 10, 0, 645, 61, 1, 0, 0, 0, 646, 647, 4, 23, 7, 0, 647, 648, 7, 13, 0, 0, 648, 649, 7, 7, 0, 0, 649, 650, 7, 7, 0, 0, 650, 651, 7, 18, 0, 0, 651, 652, 7, 20, 0, 0, 652, 653, 7, 8, 0, 0, 653, 654, 1, 0, 0, 0, 654, 655, 6, 23, 10, 0, 655, 63, 1, 0, 0, 0, 656, 658, 8, 22, 0, 0, 657, 656, 1, 0, 0, 0, 658, 659, 1, 0, 0, 0, 659, 657, 1, 0, 0, 0, 659, 660, 1, 0, 0, 0, 660, 661, 1, 0, 0, 0, 661, 662, 6, 24, 0, 0, 662, 65, 1, 0, 0, 0, 663, 664, 5, 47, 0, 0, 664, 665, 5, 47, 0, 0, 665, 669, 1, 0, 0, 0, 666, 668, 8, 23, 0, 0, 667, 666, 1, 0, 0, 0, 668, 671, 1, 0, 0, 0, 669, 667, 1, 0, 0, 0, 669, 670, 1, 0, 0, 0, 670, 673, 1, 0, 0, 0, 671, 669, 1, 0, 0, 0, 672, 674, 5, 13, 0, 0, 673, 672, 1, 0, 0, 0, 673, 674, 1, 0, 0, 0, 674, 676, 1, 0, 0, 0, 675, 677, 5, 10, 0, 0, 676, 675, 1, 0, 0, 0, 676, 677, 1, 0, 0, 0, 677, 678, 1, 0, 0, 0, 678, 679, 6, 25, 11, 0, 679, 67, 1, 0, 0, 0, 680, 681, 5, 47, 0, 0, 681, 682, 5, 42, 0, 0, 682, 687, 1, 0, 0, 0, 683, 686, 3, 68, 26, 0, 684, 686, 9, 0, 0, 0, 685, 683, 1, 0, 0, 0, 685, 684, 1, 0, 0, 0, 686, 689, 1, 0, 0, 0, 687, 688, 1, 0, 0, 0, 687, 685, 1, 0, 0, 0, 688, 690, 1, 0, 0, 0, 689, 687, 1, 0, 0, 0, 690, 691, 5, 42, 0, 0, 691, 692, 5, 47, 0, 0, 692, 693, 1, 0, 0, 0, 693, 694, 6, 26, 11, 0, 694, 69, 1, 0, 0, 0, 695, 697, 7, 24, 0, 0, 696, 695, 1, 0, 0, 0, 697, 698, 1, 0, 0, 0, 698, 696, 1, 0, 0, 0, 698, 699, 1, 0, 0, 0, 699, 700, 1, 0, 0, 0, 700, 701, 6, 27, 11, 0, 701, 71, 1, 0, 0, 0, 702, 703, 5, 124, 0, 0, 703, 704, 1, 0, 0, 0, 704, 705, 6, 28, 12, 0, 705, 73, 1, 0, 0, 0, 706, 707, 7, 25, 0, 0, 707, 75, 1, 0, 0, 0, 708, 709, 7, 26, 0, 0, 709, 77, 1, 0, 0, 0, 710, 711, 5, 92, 0, 0, 711, 712, 7, 27, 0, 0, 712, 79, 1, 0, 0, 0, 713, 714, 8, 28, 0, 0, 714, 81, 1, 0, 0, 0, 715, 717, 7, 3, 0, 0, 716, 718, 7, 29, 0, 0, 717, 716, 1, 0, 0, 0, 717, 718, 1, 0, 0, 0, 718, 720, 1, 0, 0, 0, 719, 721, 3, 74, 29, 0, 720, 719, 1, 0, 0, 0, 721, 722, 1, 0, 0, 0, 722, 720, 1, 0, 0, 0, 722, 723, 1, 0, 0, 0, 723, 83, 1, 0, 0, 0, 724, 725, 5, 64, 0, 0, 725, 85, 1, 0, 0, 0, 726, 727, 5, 96, 0, 0, 727, 87, 1, 0, 0, 0, 728, 732, 8, 30, 0, 0, 729, 730, 5, 96, 0, 0, 730, 732, 5, 96, 0, 0, 731, 728, 1, 0, 0, 0, 731, 729, 1, 0, 0, 0, 732, 89, 1, 0, 0, 0, 733, 734, 5, 95, 0, 0, 734, 91, 1, 0, 0, 0, 735, 739, 3, 76, 30, 0, 736, 739, 3, 74, 29, 0, 737, 739, 3, 90, 37, 0, 738, 735, 1, 0, 0, 0, 738, 736, 1, 0, 0, 0, 738, 737, 1, 0, 0, 0, 739, 93, 1, 0, 0, 0, 740, 745, 5, 34, 0, 0, 741, 744, 3, 78, 31, 0, 742, 744, 3, 80, 32, 0, 743, 741, 1, 0, 0, 0, 743, 742, 1, 0, 0, 0, 744, 747, 1, 0, 0, 0, 745, 743, 1, 0, 0, 0, 745, 746, 1, 0, 0, 0, 746, 748, 1, 0, 0, 0, 747, 745, 1, 0, 0, 0, 748, 770, 5, 34, 0, 0, 749, 750, 5, 34, 0, 0, 750, 751, 5, 34, 0, 0, 751, 752, 5, 34, 0, 0, 752, 756, 1, 0, 0, 0, 753, 755, 8, 23, 0, 0, 754, 753, 1, 0, 0, 0, 755, 758, 1, 0, 0, 0, 756, 757, 1, 0, 0, 0, 756, 754, 1, 0, 0, 0, 757, 759, 1, 0, 0, 0, 758, 756, 1, 0, 0, 0, 759, 760, 5, 34, 0, 0, 760, 761, 5, 34, 0, 0, 761, 762, 5, 34, 0, 0, 762, 764, 1, 0, 0, 0, 763, 765, 5, 34, 0, 0, 764, 763, 1, 0, 0, 0, 764, 765, 1, 0, 0, 0, 765, 767, 1, 0, 0, 0, 766, 768, 5, 34, 0, 0, 767, 766, 1, 0, 0, 0, 767, 768, 1, 0, 0, 0, 768, 770, 1, 0, 0, 0, 769, 740, 1, 0, 0, 0, 769, 749, 1, 0, 0, 0, 770, 95, 1, 0, 0, 0, 771, 773, 3, 74, 29, 0, 772, 771, 1, 0, 0, 0, 773, 774, 1, 0, 0, 0, 774, 772, 1, 0, 0, 0, 774, 775, 1, 0, 0, 0, 775, 97, 1, 0, 0, 0, 776, 778, 3, 74, 29, 0, 777, 776, 1, 0, 0, 0, 778, 779, 1, 0, 0, 0, 779, 777, 1, 0, 0, 0, 779, 780, 1, 0, 0, 0, 780, 781, 1, 0, 0, 0, 781, 785, 3, 116, 50, 0, 782, 784, 3, 74, 29, 0, 783, 782, 1, 0, 0, 0, 784, 787, 1, 0, 0, 0, 785, 783, 1, 0, 0, 0, 785, 786, 1, 0, 0, 0, 786, 819, 1, 0, 0, 0, 787, 785, 1, 0, 0, 0, 788, 790, 3, 116, 50, 0, 789, 791, 3, 74, 29, 0, 790, 789, 1, 0, 0, 0, 791, 792, 1, 0, 0, 0, 792, 790, 1, 0, 0, 0, 792, 793, 1, 0, 0, 0, 793, 819, 1, 0, 0, 0, 794, 796, 3, 74, 29, 0, 795, 794, 1, 0, 0, 0, 796, 797, 1, 0, 0, 0, 797, 795, 1, 0, 0, 0, 797, 798, 1, 0, 0, 0, 798, 806, 1, 0, 0, 0, 799, 803, 3, 116, 50, 0, 800, 802, 3, 74, 29, 0, 801, 800, 1, 0, 0, 0, 802, 805, 1, 0, 0, 0, 803, 801, 1, 0, 0, 0, 803, 804, 1, 0, 0, 0, 804, 807, 1, 0, 0, 0, 805, 803, 1, 0, 0, 0, 806, 799, 1, 0, 0, 0, 806, 807, 1, 0, 0, 0, 807, 808, 1, 0, 0, 0, 808, 809, 3, 82, 33, 0, 809, 819, 1, 0, 0, 0, 810, 812, 3, 116, 50, 0, 811, 813, 3, 74, 29, 0, 812, 811, 1, 0, 0, 0, 813, 814, 1, 0, 0, 0, 814, 812, 1, 0, 0, 0, 814, 815, 1, 0, 0, 0, 815, 816, 1, 0, 0, 0, 816, 817, 3, 82, 33, 0, 817, 819, 1, 0, 0, 0, 818, 777, 1, 0, 0, 0, 818, 788, 1, 0, 0, 0, 818, 795, 1, 0, 0, 0, 818, 810, 1, 0, 0, 0, 819, 99, 1, 0, 0, 0, 820, 821, 7, 31, 0, 0, 821, 822, 7, 32, 0, 0, 822, 101, 1, 0, 0, 0, 823, 824, 7, 12, 0, 0, 824, 825, 7, 9, 0, 0, 825, 826, 7, 0, 0, 0, 826, 103, 1, 0, 0, 0, 827, 828, 7, 12, 0, 0, 828, 829, 7, 2, 0, 0, 829, 830, 7, 4, 0, 0, 830, 105, 1, 0, 0, 0, 831, 832, 5, 61, 0, 0, 832, 107, 1, 0, 0, 0, 833, 834, 5, 58, 0, 0, 834, 835, 5, 58, 0, 0, 835, 109, 1, 0, 0, 0, 836, 837, 5, 58, 0, 0, 837, 111, 1, 0, 0, 0, 838, 839, 5, 44, 0, 0, 839, 113, 1, 0, 0, 0, 840, 841, 7, 0, 0, 0, 841, 842, 7, 3, 0, 0, 842, 843, 7, 2, 0, 0, 843, 844, 7, 4, 0, 0, 844, 115, 1, 0, 0, 0, 845, 846, 5, 46, 0, 0, 846, 117, 1, 0, 0, 0, 847, 848, 7, 15, 0, 0, 848, 849, 7, 12, 0, 0, 849, 850, 7, 13, 0, 0, 850, 851, 7, 2, 0, 0, 851, 852, 7, 3, 0, 0, 852, 119, 1, 0, 0, 0, 853, 854, 7, 15, 0, 0, 854, 855, 7, 1, 0, 0, 855, 856, 7, 6, 0, 0, 856, 857, 7, 2, 0, 0, 857, 858, 7, 5, 0, 0, 858, 121, 1, 0, 0, 0, 859, 860, 7, 1, 0, 0, 860, 861, 7, 9, 0, 0, 861, 123, 1, 0, 0, 0, 862, 863, 7, 1, 0, 0, 863, 864, 7, 2, 0, 0, 864, 125, 1, 0, 0, 0, 865, 866, 7, 13, 0, 0, 866, 867, 7, 12, 0, 0, 867, 868, 7, 2, 0, 0, 868, 869, 7, 5, 0, 0, 869, 127, 1, 0, 0, 0, 870, 871, 7, 13, 0, 0, 871, 872, 7, 1, 0, 0, 872, 873, 7, 18, 0, 0, 873, 874, 7, 3, 0, 0, 874, 129, 1, 0, 0, 0, 875, 876, 5, 40, 0, 0, 876, 131, 1, 0, 0, 0, 877, 878, 7, 9, 0, 0, 878, 879, 7, 7, 0, 0, 879, 880, 7, 5, 0, 0, 880, 133, 1, 0, 0, 0, 881, 882, 7, 9, 0, 0, 882, 883, 7, 20, 0, 0, 883, 884, 7, 13, 0, 0, 884, 885, 7, 13, 0, 0, 885, 135, 1, 0, 0, 0, 886, 887, 7, 9, 0, 0, 887, 888, 7, 20, 0, 0, 888, 889, 7, 13, 0, 0, 889, 890, 7, 13, 0, 0, 890, 891, 7, 2, 0, 0, 891, 137, 1, 0, 0, 0, 892, 893, 7, 7, 0, 0, 893, 894, 7, 6, 0, 0, 894, 139, 1, 0, 0, 0, 895, 896, 5, 63, 0, 0, 896, 141, 1, 0, 0, 0, 897, 898, 7, 6, 0, 0, 898, 899, 7, 13, 0, 0, 899, 900, 7, 1, 0, 0, 900, 901, 7, 18, 0, 0, 901, 902, 7, 3, 0, 0, 902, 143, 1, 0, 0, 0, 903, 904, 5, 41, 0, 0, 904, 145, 1, 0, 0, 0, 905, 906, 7, 5, 0, 0, 906, 907, 7, 6, 0, 0, 907, 908, 7, 20, 0, 0, 908, 909, 7, 3, 0, 0, 909, 147, 1, 0, 0, 0, 910, 911, 5, 61, 0, 0, 911, 912, 5, 61, 0, 0, 912, 149, 1, 0, 0, 0, 913, 914, 5, 61, 0, 0, 914, 915, 5, 126, 0, 0, 915, 151, 1, 0, 0, 0, 916, 917, 5, 33, 0, 0, 917, 918, 5, 61, 0, 0, 918, 153, 1, 0, 0, 0, 919, 920, 5, 60, 0, 0, 920, 155, 1, 0, 0, 0, 921, 922, 5, 60, 0, 0, 922, 923, 5, 61, 0, 0, 923, 157, 1, 0, 0, 0, 924, 925, 5, 62, 0, 0, 925, 159, 1, 0, 0, 0, 926, 927, 5, 62, 0, 0, 927, 928, 5, 61, 0, 0, 928, 161, 1, 0, 0, 0, 929, 930, 5, 43, 0, 0, 930, 163, 1, 0, 0, 0, 931, 932, 5, 45, 0, 0, 932, 165, 1, 0, 0, 0, 933, 934, 5, 42, 0, 0, 934, 167, 1, 0, 0, 0, 935, 936, 5, 47, 0, 0, 936, 169, 1, 0, 0, 0, 937, 938, 5, 37, 0, 0, 938, 171, 1, 0, 0, 0, 939, 940, 4, 78, 8, 0, 940, 941, 5, 123, 0, 0, 941, 173, 1, 0, 0, 0, 942, 943, 4, 79, 9, 0, 943, 944, 5, 125, 0, 0, 944, 175, 1, 0, 0, 0, 945, 946, 3, 46, 15, 0, 946, 947, 1, 0, 0, 0, 947, 948, 6, 80, 13, 0, 948, 177, 1, 0, 0, 0, 949, 952, 3, 140, 62, 0, 950, 953, 3, 76, 30, 0, 951, 953, 3, 90, 37, 0, 952, 950, 1, 0, 0, 0, 952, 951, 1, 0, 0, 0, 953, 957, 1, 0, 0, 0, 954, 956, 3, 92, 38, 0, 955, 954, 1, 0, 0, 0, 956, 959, 1, 0, 0, 0, 957, 955, 1, 0, 0, 0, 957, 958, 1, 0, 0, 0, 958, 967, 1, 0, 0, 0, 959, 957, 1, 0, 0, 0, 960, 962, 3, 140, 62, 0, 961, 963, 3, 74, 29, 0, 962, 961, 1, 0, 0, 0, 963, 964, 1, 0, 0, 0, 964, 962, 1, 0, 0, 0, 964, 965, 1, 0, 0, 0, 965, 967, 1, 0, 0, 0, 966, 949, 1, 0, 0, 0, 966, 960, 1, 0, 0, 0, 967, 179, 1, 0, 0, 0, 968, 969, 5, 91, 0, 0, 969, 970, 1, 0, 0, 0, 970, 971, 6, 82, 0, 0, 971, 972, 6, 82, 0, 0, 972, 181, 1, 0, 0, 0, 973, 974, 5, 93, 0, 0, 974, 975, 1, 0, 0, 0, 975, 976, 6, 83, 12, 0, 976, 977, 6, 83, 12, 0, 977, 183, 1, 0, 0, 0, 978, 982, 3, 76, 30, 0, 979, 981, 3, 92, 38, 0, 980, 979, 1, 0, 0, 0, 981, 984, 1, 0, 0, 0, 982, 980, 1, 0, 0, 0, 982, 983, 1, 0, 0, 0, 983, 995, 1, 0, 0, 0, 984, 982, 1, 0, 0, 0, 985, 988, 3, 90, 37, 0, 986, 988, 3, 84, 34, 0, 987, 985, 1, 0, 0, 0, 987, 986, 1, 0, 0, 0, 988, 990, 1, 0, 0, 0, 989, 991, 3, 92, 38, 0, 990, 989, 1, 0, 0, 0, 991, 992, 1, 0, 0, 0, 992, 990, 1, 0, 0, 0, 992, 993, 1, 0, 0, 0, 993, 995, 1, 0, 0, 0, 994, 978, 1, 0, 0, 0, 994, 987, 1, 0, 0, 0, 995, 185, 1, 0, 0, 0, 996, 998, 3, 86, 35, 0, 997, 999, 3, 88, 36, 0, 998, 997, 1, 0, 0, 0, 999, 1000, 1, 0, 0, 0, 1000, 998, 1, 0, 0, 0, 1000, 1001, 1, 0, 0, 0, 1001, 1002, 1, 0, 0, 0, 1002, 1003, 3, 86, 35, 0, 1003, 187, 1, 0, 0, 0, 1004, 1005, 3, 186, 85, 0, 1005, 189, 1, 0, 0, 0, 1006, 1007, 3, 66, 25, 0, 1007, 1008, 1, 0, 0, 0, 1008, 1009, 6, 87, 11, 0, 1009, 191, 1, 0, 0, 0, 1010, 1011, 3, 68, 26, 0, 1011, 1012, 1, 0, 0, 0, 1012, 1013, 6, 88, 11, 0, 1013, 193, 1, 0, 0, 0, 1014, 1015, 3, 70, 27, 0, 1015, 1016, 1, 0, 0, 0, 1016, 1017, 6, 89, 11, 0, 1017, 195, 1, 0, 0, 0, 1018, 1019, 3, 180, 82, 0, 1019, 1020, 1, 0, 0, 0, 1020, 1021, 6, 90, 14, 0, 1021, 1022, 6, 90, 15, 0, 1022, 197, 1, 0, 0, 0, 1023, 1024, 3, 72, 28, 0, 1024, 1025, 1, 0, 0, 0, 1025, 1026, 6, 91, 16, 0, 1026, 1027, 6, 91, 12, 0, 1027, 199, 1, 0, 0, 0, 1028, 1029, 3, 70, 27, 0, 1029, 1030, 1, 0, 0, 0, 1030, 1031, 6, 92, 11, 0, 1031, 201, 1, 0, 0, 0, 1032, 1033, 3, 66, 25, 0, 1033, 1034, 1, 0, 0, 0, 1034, 1035, 6, 93, 11, 0, 1035, 203, 1, 0, 0, 0, 1036, 1037, 3, 68, 26, 0, 1037, 1038, 1, 0, 0, 0, 1038, 1039, 6, 94, 11, 0, 1039, 205, 1, 0, 0, 0, 1040, 1041, 3, 72, 28, 0, 1041, 1042, 1, 0, 0, 0, 1042, 1043, 6, 95, 16, 0, 1043, 1044, 6, 95, 12, 0, 1044, 207, 1, 0, 0, 0, 1045, 1046, 3, 180, 82, 0, 1046, 1047, 1, 0, 0, 0, 1047, 1048, 6, 96, 14, 0, 1048, 209, 1, 0, 0, 0, 1049, 1050, 3, 182, 83, 0, 1050, 1051, 1, 0, 0, 0, 1051, 1052, 6, 97, 17, 0, 1052, 211, 1, 0, 0, 0, 1053, 1054, 3, 110, 47, 0, 1054, 1055, 1, 0, 0, 0, 1055, 1056, 6, 98, 18, 0, 1056, 213, 1, 0, 0, 0, 1057, 1058, 3, 112, 48, 0, 1058, 1059, 1, 0, 0, 0, 1059, 1060, 6, 99, 19, 0, 1060, 215, 1, 0, 0, 0, 1061, 1062, 3, 106, 45, 0, 1062, 1063, 1, 0, 0, 0, 1063, 1064, 6, 100, 20, 0, 1064, 217, 1, 0, 0, 0, 1065, 1066, 7, 16, 0, 0, 1066, 1067, 7, 3, 0, 0, 1067, 1068, 7, 5, 0, 0, 1068, 1069, 7, 12, 0, 0, 1069, 1070, 7, 0, 0, 0, 1070, 1071, 7, 12, 0, 0, 1071, 1072, 7, 5, 0, 0, 1072, 1073, 7, 12, 0, 0, 1073, 219, 1, 0, 0, 0, 1074, 1078, 8, 33, 0, 0, 1075, 1076, 5, 47, 0, 0, 1076, 1078, 8, 34, 0, 0, 1077, 1074, 1, 0, 0, 0, 1077, 1075, 1, 0, 0, 0, 1078, 221, 1, 0, 0, 0, 1079, 1081, 3, 220, 102, 0, 1080, 1079, 1, 0, 0, 0, 1081, 1082, 1, 0, 0, 0, 1082, 1080, 1, 0, 0, 0, 1082, 1083, 1, 0, 0, 0, 1083, 223, 1, 0, 0, 0, 1084, 1085, 3, 222, 103, 0, 1085, 1086, 1, 0, 0, 0, 1086, 1087, 6, 104, 21, 0, 1087, 225, 1, 0, 0, 0, 1088, 1089, 3, 94, 39, 0, 1089, 1090, 1, 0, 0, 0, 1090, 1091, 6, 105, 22, 0, 1091, 227, 1, 0, 0, 0, 1092, 1093, 3, 66, 25, 0, 1093, 1094, 1, 0, 0, 0, 1094, 1095, 6, 106, 11, 0, 1095, 229, 1, 0, 0, 0, 1096, 1097, 3, 68, 26, 0, 1097, 1098, 1, 0, 0, 0, 1098, 1099, 6, 107, 11, 0, 1099, 231, 1, 0, 0, 0, 1100, 1101, 3, 70, 27, 0, 1101, 1102, 1, 0, 0, 0, 1102, 1103, 6, 108, 11, 0, 1103, 233, 1, 0, 0, 0, 1104, 1105, 3, 72, 28, 0, 1105, 1106, 1, 0, 0, 0, 1106, 1107, 6, 109, 16, 0, 1107, 1108, 6, 109, 12, 0, 1108, 235, 1, 0, 0, 0, 1109, 1110, 3, 116, 50, 0, 1110, 1111, 1, 0, 0, 0, 1111, 1112, 6, 110, 23, 0, 1112, 237, 1, 0, 0, 0, 1113, 1114, 3, 112, 48, 0, 1114, 1115, 1, 0, 0, 0, 1115, 1116, 6, 111, 19, 0, 1116, 239, 1, 0, 0, 0, 1117, 1118, 4, 112, 10, 0, 1118, 1119, 3, 140, 62, 0, 1119, 1120, 1, 0, 0, 0, 1120, 1121, 6, 112, 24, 0, 1121, 241, 1, 0, 0, 0, 1122, 1123, 4, 113, 11, 0, 1123, 1124, 3, 178, 81, 0, 1124, 1125, 1, 0, 0, 0, 1125, 1126, 6, 113, 25, 0, 1126, 243, 1, 0, 0, 0, 1127, 1132, 3, 76, 30, 0, 1128, 1132, 3, 74, 29, 0, 1129, 1132, 3, 90, 37, 0, 1130, 1132, 3, 166, 75, 0, 1131, 1127, 1, 0, 0, 0, 1131, 1128, 1, 0, 0, 0, 1131, 1129, 1, 0, 0, 0, 1131, 1130, 1, 0, 0, 0, 1132, 245, 1, 0, 0, 0, 1133, 1136, 3, 76, 30, 0, 1134, 1136, 3, 166, 75, 0, 1135, 1133, 1, 0, 0, 0, 1135, 1134, 1, 0, 0, 0, 1136, 1140, 1, 0, 0, 0, 1137, 1139, 3, 244, 114, 0, 1138, 1137, 1, 0, 0, 0, 1139, 1142, 1, 0, 0, 0, 1140, 1138, 1, 0, 0, 0, 1140, 1141, 1, 0, 0, 0, 1141, 1153, 1, 0, 0, 0, 1142, 1140, 1, 0, 0, 0, 1143, 1146, 3, 90, 37, 0, 1144, 1146, 3, 84, 34, 0, 1145, 1143, 1, 0, 0, 0, 1145, 1144, 1, 0, 0, 0, 1146, 1148, 1, 0, 0, 0, 1147, 1149, 3, 244, 114, 0, 1148, 1147, 1, 0, 0, 0, 1149, 1150, 1, 0, 0, 0, 1150, 1148, 1, 0, 0, 0, 1150, 1151, 1, 0, 0, 0, 1151, 1153, 1, 0, 0, 0, 1152, 1135, 1, 0, 0, 0, 1152, 1145, 1, 0, 0, 0, 1153, 247, 1, 0, 0, 0, 1154, 1157, 3, 246, 115, 0, 1155, 1157, 3, 186, 85, 0, 1156, 1154, 1, 0, 0, 0, 1156, 1155, 1, 0, 0, 0, 1157, 1158, 1, 0, 0, 0, 1158, 1156, 1, 0, 0, 0, 1158, 1159, 1, 0, 0, 0, 1159, 249, 1, 0, 0, 0, 1160, 1161, 3, 66, 25, 0, 1161, 1162, 1, 0, 0, 0, 1162, 1163, 6, 117, 11, 0, 1163, 251, 1, 0, 0, 0, 1164, 1165, 3, 68, 26, 0, 1165, 1166, 1, 0, 0, 0, 1166, 1167, 6, 118, 11, 0, 1167, 253, 1, 0, 0, 0, 1168, 1169, 3, 70, 27, 0, 1169, 1170, 1, 0, 0, 0, 1170, 1171, 6, 119, 11, 0, 1171, 255, 1, 0, 0, 0, 1172, 1173, 3, 72, 28, 0, 1173, 1174, 1, 0, 0, 0, 1174, 1175, 6, 120, 16, 0, 1175, 1176, 6, 120, 12, 0, 1176, 257, 1, 0, 0, 0, 1177, 1178, 3, 106, 45, 0, 1178, 1179, 1, 0, 0, 0, 1179, 1180, 6, 121, 20, 0, 1180, 259, 1, 0, 0, 0, 1181, 1182, 3, 112, 48, 0, 1182, 1183, 1, 0, 0, 0, 1183, 1184, 6, 122, 19, 0, 1184, 261, 1, 0, 0, 0, 1185, 1186, 3, 116, 50, 0, 1186, 1187, 1, 0, 0, 0, 1187, 1188, 6, 123, 23, 0, 1188, 263, 1, 0, 0, 0, 1189, 1190, 4, 124, 12, 0, 1190, 1191, 3, 140, 62, 0, 1191, 1192, 1, 0, 0, 0, 1192, 1193, 6, 124, 24, 0, 1193, 265, 1, 0, 0, 0, 1194, 1195, 4, 125, 13, 0, 1195, 1196, 3, 178, 81, 0, 1196, 1197, 1, 0, 0, 0, 1197, 1198, 6, 125, 25, 0, 1198, 267, 1, 0, 0, 0, 1199, 1200, 7, 12, 0, 0, 1200, 1201, 7, 2, 0, 0, 1201, 269, 1, 0, 0, 0, 1202, 1203, 3, 248, 116, 0, 1203, 1204, 1, 0, 0, 0, 1204, 1205, 6, 127, 26, 0, 1205, 271, 1, 0, 0, 0, 1206, 1207, 3, 66, 25, 0, 1207, 1208, 1, 0, 0, 0, 1208, 1209, 6, 128, 11, 0, 1209, 273, 1, 0, 0, 0, 1210, 1211, 3, 68, 26, 0, 1211, 1212, 1, 0, 0, 0, 1212, 1213, 6, 129, 11, 0, 1213, 275, 1, 0, 0, 0, 1214, 1215, 3, 70, 27, 0, 1215, 1216, 1, 0, 0, 0, 1216, 1217, 6, 130, 11, 0, 1217, 277, 1, 0, 0, 0, 1218, 1219, 3, 72, 28, 0, 1219, 1220, 1, 0, 0, 0, 1220, 1221, 6, 131, 16, 0, 1221, 1222, 6, 131, 12, 0, 1222, 279, 1, 0, 0, 0, 1223, 1224, 3, 180, 82, 0, 1224, 1225, 1, 0, 0, 0, 1225, 1226, 6, 132, 14, 0, 1226, 1227, 6, 132, 27, 0, 1227, 281, 1, 0, 0, 0, 1228, 1229, 7, 7, 0, 0, 1229, 1230, 7, 9, 0, 0, 1230, 1231, 1, 0, 0, 0, 1231, 1232, 6, 133, 28, 0, 1232, 283, 1, 0, 0, 0, 1233, 1234, 7, 19, 0, 0, 1234, 1235, 7, 1, 0, 0, 1235, 1236, 7, 5, 0, 0, 1236, 1237, 7, 10, 0, 0, 1237, 1238, 1, 0, 0, 0, 1238, 1239, 6, 134, 28, 0, 1239, 285, 1, 0, 0, 0, 1240, 1241, 8, 35, 0, 0, 1241, 287, 1, 0, 0, 0, 1242, 1244, 3, 286, 135, 0, 1243, 1242, 1, 0, 0, 0, 1244, 1245, 1, 0, 0, 0, 1245, 1243, 1, 0, 0, 0, 1245, 1246, 1, 0, 0, 0, 1246, 1247, 1, 0, 0, 0, 1247, 1248, 3, 110, 47, 0, 1248, 1250, 1, 0, 0, 0, 1249, 1243, 1, 0, 0, 0, 1249, 1250, 1, 0, 0, 0, 1250, 1252, 1, 0, 0, 0, 1251, 1253, 3, 286, 135, 0, 1252, 1251, 1, 0, 0, 0, 1253, 1254, 1, 0, 0, 0, 1254, 1252, 1, 0, 0, 0, 1254, 1255, 1, 0, 0, 0, 1255, 289, 1, 0, 0, 0, 1256, 1257, 3, 288, 136, 0, 1257, 1258, 1, 0, 0, 0, 1258, 1259, 6, 137, 29, 0, 1259, 291, 1, 0, 0, 0, 1260, 1261, 3, 66, 25, 0, 1261, 1262, 1, 0, 0, 0, 1262, 1263, 6, 138, 11, 0, 1263, 293, 1, 0, 0, 0, 1264, 1265, 3, 68, 26, 0, 1265, 1266, 1, 0, 0, 0, 1266, 1267, 6, 139, 11, 0, 1267, 295, 1, 0, 0, 0, 1268, 1269, 3, 70, 27, 0, 1269, 1270, 1, 0, 0, 0, 1270, 1271, 6, 140, 11, 0, 1271, 297, 1, 0, 0, 0, 1272, 1273, 3, 72, 28, 0, 1273, 1274, 1, 0, 0, 0, 1274, 1275, 6, 141, 16, 0, 1275, 1276, 6, 141, 12, 0, 1276, 1277, 6, 141, 12, 0, 1277, 299, 1, 0, 0, 0, 1278, 1279, 3, 106, 45, 0, 1279, 1280, 1, 0, 0, 0, 1280, 1281, 6, 142, 20, 0, 1281, 301, 1, 0, 0, 0, 1282, 1283, 3, 112, 48, 0, 1283, 1284, 1, 0, 0, 0, 1284, 1285, 6, 143, 19, 0, 1285, 303, 1, 0, 0, 0, 1286, 1287, 3, 116, 50, 0, 1287, 1288, 1, 0, 0, 0, 1288, 1289, 6, 144, 23, 0, 1289, 305, 1, 0, 0, 0, 1290, 1291, 3, 284, 134, 0, 1291, 1292, 1, 0, 0, 0, 1292, 1293, 6, 145, 30, 0, 1293, 307, 1, 0, 0, 0, 1294, 1295, 3, 248, 116, 0, 1295, 1296, 1, 0, 0, 0, 1296, 1297, 6, 146, 26, 0, 1297, 309, 1, 0, 0, 0, 1298, 1299, 3, 188, 86, 0, 1299, 1300, 1, 0, 0, 0, 1300, 1301, 6, 147, 31, 0, 1301, 311, 1, 0, 0, 0, 1302, 1303, 4, 148, 14, 0, 1303, 1304, 3, 140, 62, 0, 1304, 1305, 1, 0, 0, 0, 1305, 1306, 6, 148, 24, 0, 1306, 313, 1, 0, 0, 0, 1307, 1308, 4, 149, 15, 0, 1308, 1309, 3, 178, 81, 0, 1309, 1310, 1, 0, 0, 0, 1310, 1311, 6, 149, 25, 0, 1311, 315, 1, 0, 0, 0, 1312, 1313, 3, 66, 25, 0, 1313, 1314, 1, 0, 0, 0, 1314, 1315, 6, 150, 11, 0, 1315, 317, 1, 0, 0, 0, 1316, 1317, 3, 68, 26, 0, 1317, 1318, 1, 0, 0, 0, 1318, 1319, 6, 151, 11, 0, 1319, 319, 1, 0, 0, 0, 1320, 1321, 3, 70, 27, 0, 1321, 1322, 1, 0, 0, 0, 1322, 1323, 6, 152, 11, 0, 1323, 321, 1, 0, 0, 0, 1324, 1325, 3, 72, 28, 0, 1325, 1326, 1, 0, 0, 0, 1326, 1327, 6, 153, 16, 0, 1327, 1328, 6, 153, 12, 0, 1328, 323, 1, 0, 0, 0, 1329, 1330, 3, 116, 50, 0, 1330, 1331, 1, 0, 0, 0, 1331, 1332, 6, 154, 23, 0, 1332, 325, 1, 0, 0, 0, 1333, 1334, 4, 155, 16, 0, 1334, 1335, 3, 140, 62, 0, 1335, 1336, 1, 0, 0, 0, 1336, 1337, 6, 155, 24, 0, 1337, 327, 1, 0, 0, 0, 1338, 1339, 4, 156, 17, 0, 1339, 1340, 3, 178, 81, 0, 1340, 1341, 1, 0, 0, 0, 1341, 1342, 6, 156, 25, 0, 1342, 329, 1, 0, 0, 0, 1343, 1344, 3, 188, 86, 0, 1344, 1345, 1, 0, 0, 0, 1345, 1346, 6, 157, 31, 0, 1346, 331, 1, 0, 0, 0, 1347, 1348, 3, 184, 84, 0, 1348, 1349, 1, 0, 0, 0, 1349, 1350, 6, 158, 32, 0, 1350, 333, 1, 0, 0, 0, 1351, 1352, 3, 66, 25, 0, 1352, 1353, 1, 0, 0, 0, 1353, 1354, 6, 159, 11, 0, 1354, 335, 1, 0, 0, 0, 1355, 1356, 3, 68, 26, 0, 1356, 1357, 1, 0, 0, 0, 1357, 1358, 6, 160, 11, 0, 1358, 337, 1, 0, 0, 0, 1359, 1360, 3, 70, 27, 0, 1360, 1361, 1, 0, 0, 0, 1361, 1362, 6, 161, 11, 0, 1362, 339, 1, 0, 0, 0, 1363, 1364, 3, 72, 28, 0, 1364, 1365, 1, 0, 0, 0, 1365, 1366, 6, 162, 16, 0, 1366, 1367, 6, 162, 12, 0, 1367, 341, 1, 0, 0, 0, 1368, 1369, 7, 1, 0, 0, 1369, 1370, 7, 9, 0, 0, 1370, 1371, 7, 15, 0, 0, 1371, 1372, 7, 7, 0, 0, 1372, 343, 1, 0, 0, 0, 1373, 1374, 3, 66, 25, 0, 1374, 1375, 1, 0, 0, 0, 1375, 1376, 6, 164, 11, 0, 1376, 345, 1, 0, 0, 0, 1377, 1378, 3, 68, 26, 0, 1378, 1379, 1, 0, 0, 0, 1379, 1380, 6, 165, 11, 0, 1380, 347, 1, 0, 0, 0, 1381, 1382, 3, 70, 27, 0, 1382, 1383, 1, 0, 0, 0, 1383, 1384, 6, 166, 11, 0, 1384, 349, 1, 0, 0, 0, 1385, 1386, 3, 182, 83, 0, 1386, 1387, 1, 0, 0, 0, 1387, 1388, 6, 167, 17, 0, 1388, 1389, 6, 167, 12, 0, 1389, 351, 1, 0, 0, 0, 1390, 1391, 3, 110, 47, 0, 1391, 1392, 1, 0, 0, 0, 1392, 1393, 6, 168, 18, 0, 1393, 353, 1, 0, 0, 0, 1394, 1400, 3, 84, 34, 0, 1395, 1400, 3, 74, 29, 0, 1396, 1400, 3, 116, 50, 0, 1397, 1400, 3, 76, 30, 0, 1398, 1400, 3, 90, 37, 0, 1399, 1394, 1, 0, 0, 0, 1399, 1395, 1, 0, 0, 0, 1399, 1396, 1, 0, 0, 0, 1399, 1397, 1, 0, 0, 0, 1399, 1398, 1, 0, 0, 0, 1400, 1401, 1, 0, 0, 0, 1401, 1399, 1, 0, 0, 0, 1401, 1402, 1, 0, 0, 0, 1402, 355, 1, 0, 0, 0, 1403, 1404, 3, 66, 25, 0, 1404, 1405, 1, 0, 0, 0, 1405, 1406, 6, 170, 11, 0, 1406, 357, 1, 0, 0, 0, 1407, 1408, 3, 68, 26, 0, 1408, 1409, 1, 0, 0, 0, 1409, 1410, 6, 171, 11, 0, 1410, 359, 1, 0, 0, 0, 1411, 1412, 3, 70, 27, 0, 1412, 1413, 1, 0, 0, 0, 1413, 1414, 6, 172, 11, 0, 1414, 361, 1, 0, 0, 0, 1415, 1416, 3, 72, 28, 0, 1416, 1417, 1, 0, 0, 0, 1417, 1418, 6, 173, 16, 0, 1418, 1419, 6, 173, 12, 0, 1419, 363, 1, 0, 0, 0, 1420, 1421, 3, 110, 47, 0, 1421, 1422, 1, 0, 0, 0, 1422, 1423, 6, 174, 18, 0, 1423, 365, 1, 0, 0, 0, 1424, 1425, 3, 112, 48, 0, 1425, 1426, 1, 0, 0, 0, 1426, 1427, 6, 175, 19, 0, 1427, 367, 1, 0, 0, 0, 1428, 1429, 3, 116, 50, 0, 1429, 1430, 1, 0, 0, 0, 1430, 1431, 6, 176, 23, 0, 1431, 369, 1, 0, 0, 0, 1432, 1433, 3, 282, 133, 0, 1433, 1434, 1, 0, 0, 0, 1434, 1435, 6, 177, 33, 0, 1435, 1436, 6, 177, 34, 0, 1436, 371, 1, 0, 0, 0, 1437, 1438, 3, 222, 103, 0, 1438, 1439, 1, 0, 0, 0, 1439, 1440, 6, 178, 21, 0, 1440, 373, 1, 0, 0, 0, 1441, 1442, 3, 94, 39, 0, 1442, 1443, 1, 0, 0, 0, 1443, 1444, 6, 179, 22, 0, 1444, 375, 1, 0, 0, 0, 1445, 1446, 3, 66, 25, 0, 1446, 1447, 1, 0, 0, 0, 1447, 1448, 6, 180, 11, 0, 1448, 377, 1, 0, 0, 0, 1449, 1450, 3, 68, 26, 0, 1450, 1451, 1, 0, 0, 0, 1451, 1452, 6, 181, 11, 0, 1452, 379, 1, 0, 0, 0, 1453, 1454, 3, 70, 27, 0, 1454, 1455, 1, 0, 0, 0, 1455, 1456, 6, 182, 11, 0, 1456, 381, 1, 0, 0, 0, 1457, 1458, 3, 72, 28, 0, 1458, 1459, 1, 0, 0, 0, 1459, 1460, 6, 183, 16, 0, 1460, 1461, 6, 183, 12, 0, 1461, 1462, 6, 183, 12, 0, 1462, 383, 1, 0, 0, 0, 1463, 1464, 3, 112, 48, 0, 1464, 1465, 1, 0, 0, 0, 1465, 1466, 6, 184, 19, 0, 1466, 385, 1, 0, 0, 0, 1467, 1468, 3, 116, 50, 0, 1468, 1469, 1, 0, 0, 0, 1469, 1470, 6, 185, 23, 0, 1470, 387, 1, 0, 0, 0, 1471, 1472, 3, 248, 116, 0, 1472, 1473, 1, 0, 0, 0, 1473, 1474, 6, 186, 26, 0, 1474, 389, 1, 0, 0, 0, 1475, 1476, 3, 66, 25, 0, 1476, 1477, 1, 0, 0, 0, 1477, 1478, 6, 187, 11, 0, 1478, 391, 1, 0, 0, 0, 1479, 1480, 3, 68, 26, 0, 1480, 1481, 1, 0, 0, 0, 1481, 1482, 6, 188, 11, 0, 1482, 393, 1, 0, 0, 0, 1483, 1484, 3, 70, 27, 0, 1484, 1485, 1, 0, 0, 0, 1485, 1486, 6, 189, 11, 0, 1486, 395, 1, 0, 0, 0, 1487, 1488, 3, 72, 28, 0, 1488, 1489, 1, 0, 0, 0, 1489, 1490, 6, 190, 16, 0, 1490, 1491, 6, 190, 12, 0, 1491, 397, 1, 0, 0, 0, 1492, 1493, 3, 54, 19, 0, 1493, 1494, 1, 0, 0, 0, 1494, 1495, 6, 191, 35, 0, 1495, 399, 1, 0, 0, 0, 1496, 1497, 3, 268, 126, 0, 1497, 1498, 1, 0, 0, 0, 1498, 1499, 6, 192, 36, 0, 1499, 401, 1, 0, 0, 0, 1500, 1501, 3, 282, 133, 0, 1501, 1502, 1, 0, 0, 0, 1502, 1503, 6, 193, 33, 0, 1503, 1504, 6, 193, 12, 0, 1504, 1505, 6, 193, 0, 0, 1505, 403, 1, 0, 0, 0, 1506, 1507, 7, 20, 0, 0, 1507, 1508, 7, 2, 0, 0, 1508, 1509, 7, 1, 0, 0, 1509, 1510, 7, 9, 0, 0, 1510, 1511, 7, 17, 0, 0, 1511, 1512, 1, 0, 0, 0, 1512, 1513, 6, 194, 12, 0, 1513, 1514, 6, 194, 0, 0, 1514, 405, 1, 0, 0, 0, 1515, 1516, 3, 184, 84, 0, 1516, 1517, 1, 0, 0, 0, 1517, 1518, 6, 195, 32, 0, 1518, 407, 1, 0, 0, 0, 1519, 1520, 3, 188, 86, 0, 1520, 1521, 1, 0, 0, 0, 1521, 1522, 6, 196, 31, 0, 1522, 409, 1, 0, 0, 0, 1523, 1524, 3, 66, 25, 0, 1524, 1525, 1, 0, 0, 0, 1525, 1526, 6, 197, 11, 0, 1526, 411, 1, 0, 0, 0, 1527, 1528, 3, 68, 26, 0, 1528, 1529, 1, 0, 0, 0, 1529, 1530, 6, 198, 11, 0, 1530, 413, 1, 0, 0, 0, 1531, 1532, 3, 70, 27, 0, 1532, 1533, 1, 0, 0, 0, 1533, 1534, 6, 199, 11, 0, 1534, 415, 1, 0, 0, 0, 1535, 1536, 3, 72, 28, 0, 1536, 1537, 1, 0, 0, 0, 1537, 1538, 6, 200, 16, 0, 1538, 1539, 6, 200, 12, 0, 1539, 417, 1, 0, 0, 0, 1540, 1541, 3, 222, 103, 0, 1541, 1542, 1, 0, 0, 0, 1542, 1543, 6, 201, 21, 0, 1543, 1544, 6, 201, 12, 0, 1544, 1545, 6, 201, 37, 0, 1545, 419, 1, 0, 0, 0, 1546, 1547, 3, 94, 39, 0, 1547, 1548, 1, 0, 0, 0, 1548, 1549, 6, 202, 22, 0, 1549, 1550, 6, 202, 12, 0, 1550, 1551, 6, 202, 37, 0, 1551, 421, 1, 0, 0, 0, 1552, 1553, 3, 66, 25, 0, 1553, 1554, 1, 0, 0, 0, 1554, 1555, 6, 203, 11, 0, 1555, 423, 1, 0, 0, 0, 1556, 1557, 3, 68, 26, 0, 1557, 1558, 1, 0, 0, 0, 1558, 1559, 6, 204, 11, 0, 1559, 425, 1, 0, 0, 0, 1560, 1561, 3, 70, 27, 0, 1561, 1562, 1, 0, 0, 0, 1562, 1563, 6, 205, 11, 0, 1563, 427, 1, 0, 0, 0, 1564, 1565, 3, 110, 47, 0, 1565, 1566, 1, 0, 0, 0, 1566, 1567, 6, 206, 18, 0, 1567, 1568, 6, 206, 12, 0, 1568, 1569, 6, 206, 9, 0, 1569, 429, 1, 0, 0, 0, 1570, 1571, 3, 112, 48, 0, 1571, 1572, 1, 0, 0, 0, 1572, 1573, 6, 207, 19, 0, 1573, 1574, 6, 207, 12, 0, 1574, 1575, 6, 207, 9, 0, 1575, 431, 1, 0, 0, 0, 1576, 1577, 3, 66, 25, 0, 1577, 1578, 1, 0, 0, 0, 1578, 1579, 6, 208, 11, 0, 1579, 433, 1, 0, 0, 0, 1580, 1581, 3, 68, 26, 0, 1581, 1582, 1, 0, 0, 0, 1582, 1583, 6, 209, 11, 0, 1583, 435, 1, 0, 0, 0, 1584, 1585, 3, 70, 27, 0, 1585, 1586, 1, 0, 0, 0, 1586, 1587, 6, 210, 11, 0, 1587, 437, 1, 0, 0, 0, 1588, 1589, 3, 188, 86, 0, 1589, 1590, 1, 0, 0, 0, 1590, 1591, 6, 211, 12, 0, 1591, 1592, 6, 211, 0, 0, 1592, 1593, 6, 211, 31, 0, 1593, 439, 1, 0, 0, 0, 1594, 1595, 3, 184, 84, 0, 1595, 1596, 1, 0, 0, 0, 1596, 1597, 6, 212, 12, 0, 1597, 1598, 6, 212, 0, 0, 1598, 1599, 6, 212, 32, 0, 1599, 441, 1, 0, 0, 0, 1600, 1601, 3, 100, 42, 0, 1601, 1602, 1, 0, 0, 0, 1602, 1603, 6, 213, 12, 0, 1603, 1604, 6, 213, 0, 0, 1604, 1605, 6, 213, 38, 0, 1605, 443, 1, 0, 0, 0, 1606, 1607, 3, 72, 28, 0, 1607, 1608, 1, 0, 0, 0, 1608, 1609, 6, 214, 16, 0, 1609, 1610, 6, 214, 12, 0, 1610, 445, 1, 0, 0, 0, 66, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 659, 669, 673, 676, 685, 687, 698, 717, 722, 731, 738, 743, 745, 756, 764, 767, 769, 774, 779, 785, 792, 797, 803, 806, 814, 818, 952, 957, 964, 966, 982, 987, 992, 994, 1000, 1077, 1082, 1131, 1135, 1140, 1145, 1150, 1152, 1156, 1158, 1245, 1249, 1254, 1399, 1401, 39, 5, 1, 0, 5, 4, 0, 5, 6, 0, 5, 2, 0, 5, 3, 0, 5, 8, 0, 5, 5, 0, 5, 9, 0, 5, 11, 0, 5, 14, 0, 5, 13, 0, 0, 1, 0, 4, 0, 0, 7, 16, 0, 7, 72, 0, 5, 0, 0, 7, 29, 0, 7, 73, 0, 7, 38, 0, 7, 39, 0, 7, 36, 0, 7, 83, 0, 7, 30, 0, 7, 41, 0, 7, 53, 0, 7, 71, 0, 7, 87, 0, 5, 10, 0, 5, 7, 0, 7, 97, 0, 7, 96, 0, 7, 75, 0, 7, 74, 0, 7, 95, 0, 5, 12, 0, 7, 20, 0, 7, 91, 0, 5, 15, 0, 7, 33, 0] \ No newline at end of file +[4, 0, 130, 1629, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67, 2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2, 73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 2, 78, 7, 78, 2, 79, 7, 79, 2, 80, 7, 80, 2, 81, 7, 81, 2, 82, 7, 82, 2, 83, 7, 83, 2, 84, 7, 84, 2, 85, 7, 85, 2, 86, 7, 86, 2, 87, 7, 87, 2, 88, 7, 88, 2, 89, 7, 89, 2, 90, 7, 90, 2, 91, 7, 91, 2, 92, 7, 92, 2, 93, 7, 93, 2, 94, 7, 94, 2, 95, 7, 95, 2, 96, 7, 96, 2, 97, 7, 97, 2, 98, 7, 98, 2, 99, 7, 99, 2, 100, 7, 100, 2, 101, 7, 101, 2, 102, 7, 102, 2, 103, 7, 103, 2, 104, 7, 104, 2, 105, 7, 105, 2, 106, 7, 106, 2, 107, 7, 107, 2, 108, 7, 108, 2, 109, 7, 109, 2, 110, 7, 110, 2, 111, 7, 111, 2, 112, 7, 112, 2, 113, 7, 113, 2, 114, 7, 114, 2, 115, 7, 115, 2, 116, 7, 116, 2, 117, 7, 117, 2, 118, 7, 118, 2, 119, 7, 119, 2, 120, 7, 120, 2, 121, 7, 121, 2, 122, 7, 122, 2, 123, 7, 123, 2, 124, 7, 124, 2, 125, 7, 125, 2, 126, 7, 126, 2, 127, 7, 127, 2, 128, 7, 128, 2, 129, 7, 129, 2, 130, 7, 130, 2, 131, 7, 131, 2, 132, 7, 132, 2, 133, 7, 133, 2, 134, 7, 134, 2, 135, 7, 135, 2, 136, 7, 136, 2, 137, 7, 137, 2, 138, 7, 138, 2, 139, 7, 139, 2, 140, 7, 140, 2, 141, 7, 141, 2, 142, 7, 142, 2, 143, 7, 143, 2, 144, 7, 144, 2, 145, 7, 145, 2, 146, 7, 146, 2, 147, 7, 147, 2, 148, 7, 148, 2, 149, 7, 149, 2, 150, 7, 150, 2, 151, 7, 151, 2, 152, 7, 152, 2, 153, 7, 153, 2, 154, 7, 154, 2, 155, 7, 155, 2, 156, 7, 156, 2, 157, 7, 157, 2, 158, 7, 158, 2, 159, 7, 159, 2, 160, 7, 160, 2, 161, 7, 161, 2, 162, 7, 162, 2, 163, 7, 163, 2, 164, 7, 164, 2, 165, 7, 165, 2, 166, 7, 166, 2, 167, 7, 167, 2, 168, 7, 168, 2, 169, 7, 169, 2, 170, 7, 170, 2, 171, 7, 171, 2, 172, 7, 172, 2, 173, 7, 173, 2, 174, 7, 174, 2, 175, 7, 175, 2, 176, 7, 176, 2, 177, 7, 177, 2, 178, 7, 178, 2, 179, 7, 179, 2, 180, 7, 180, 2, 181, 7, 181, 2, 182, 7, 182, 2, 183, 7, 183, 2, 184, 7, 184, 2, 185, 7, 185, 2, 186, 7, 186, 2, 187, 7, 187, 2, 188, 7, 188, 2, 189, 7, 189, 2, 190, 7, 190, 2, 191, 7, 191, 2, 192, 7, 192, 2, 193, 7, 193, 2, 194, 7, 194, 2, 195, 7, 195, 2, 196, 7, 196, 2, 197, 7, 197, 2, 198, 7, 198, 2, 199, 7, 199, 2, 200, 7, 200, 2, 201, 7, 201, 2, 202, 7, 202, 2, 203, 7, 203, 2, 204, 7, 204, 2, 205, 7, 205, 2, 206, 7, 206, 2, 207, 7, 207, 2, 208, 7, 208, 2, 209, 7, 209, 2, 210, 7, 210, 2, 211, 7, 211, 2, 212, 7, 212, 2, 213, 7, 213, 2, 214, 7, 214, 2, 215, 7, 215, 2, 216, 7, 216, 2, 217, 7, 217, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 24, 4, 24, 664, 8, 24, 11, 24, 12, 24, 665, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 1, 25, 5, 25, 674, 8, 25, 10, 25, 12, 25, 677, 9, 25, 1, 25, 3, 25, 680, 8, 25, 1, 25, 3, 25, 683, 8, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 5, 26, 692, 8, 26, 10, 26, 12, 26, 695, 9, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 27, 4, 27, 703, 8, 27, 11, 27, 12, 27, 704, 1, 27, 1, 27, 1, 28, 1, 28, 1, 28, 1, 28, 1, 29, 1, 29, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 1, 32, 1, 32, 1, 33, 1, 33, 3, 33, 724, 8, 33, 1, 33, 4, 33, 727, 8, 33, 11, 33, 12, 33, 728, 1, 34, 1, 34, 1, 35, 1, 35, 1, 36, 1, 36, 1, 36, 3, 36, 738, 8, 36, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 3, 38, 745, 8, 38, 1, 39, 1, 39, 1, 39, 5, 39, 750, 8, 39, 10, 39, 12, 39, 753, 9, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 5, 39, 761, 8, 39, 10, 39, 12, 39, 764, 9, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 3, 39, 771, 8, 39, 1, 39, 3, 39, 774, 8, 39, 3, 39, 776, 8, 39, 1, 40, 4, 40, 779, 8, 40, 11, 40, 12, 40, 780, 1, 41, 4, 41, 784, 8, 41, 11, 41, 12, 41, 785, 1, 41, 1, 41, 5, 41, 790, 8, 41, 10, 41, 12, 41, 793, 9, 41, 1, 41, 1, 41, 4, 41, 797, 8, 41, 11, 41, 12, 41, 798, 1, 41, 4, 41, 802, 8, 41, 11, 41, 12, 41, 803, 1, 41, 1, 41, 5, 41, 808, 8, 41, 10, 41, 12, 41, 811, 9, 41, 3, 41, 813, 8, 41, 1, 41, 1, 41, 1, 41, 1, 41, 4, 41, 819, 8, 41, 11, 41, 12, 41, 820, 1, 41, 1, 41, 3, 41, 825, 8, 41, 1, 42, 1, 42, 1, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 44, 1, 44, 1, 44, 1, 44, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 50, 1, 50, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 54, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 57, 1, 57, 1, 58, 1, 58, 1, 58, 1, 58, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 61, 1, 61, 1, 61, 1, 62, 1, 62, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 64, 1, 64, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 66, 1, 66, 1, 66, 1, 67, 1, 67, 1, 67, 1, 68, 1, 68, 1, 68, 1, 69, 1, 69, 1, 70, 1, 70, 1, 70, 1, 71, 1, 71, 1, 72, 1, 72, 1, 72, 1, 73, 1, 73, 1, 74, 1, 74, 1, 75, 1, 75, 1, 76, 1, 76, 1, 77, 1, 77, 1, 78, 1, 78, 1, 78, 1, 79, 1, 79, 1, 79, 1, 80, 1, 80, 1, 80, 1, 80, 1, 81, 1, 81, 1, 81, 3, 81, 959, 8, 81, 1, 81, 5, 81, 962, 8, 81, 10, 81, 12, 81, 965, 9, 81, 1, 81, 1, 81, 4, 81, 969, 8, 81, 11, 81, 12, 81, 970, 3, 81, 973, 8, 81, 1, 82, 1, 82, 1, 82, 1, 82, 1, 82, 1, 83, 1, 83, 1, 83, 1, 83, 1, 83, 1, 84, 1, 84, 5, 84, 987, 8, 84, 10, 84, 12, 84, 990, 9, 84, 1, 84, 1, 84, 3, 84, 994, 8, 84, 1, 84, 4, 84, 997, 8, 84, 11, 84, 12, 84, 998, 3, 84, 1001, 8, 84, 1, 85, 1, 85, 4, 85, 1005, 8, 85, 11, 85, 12, 85, 1006, 1, 85, 1, 85, 1, 86, 1, 86, 1, 87, 1, 87, 1, 87, 1, 87, 1, 88, 1, 88, 1, 88, 1, 88, 1, 89, 1, 89, 1, 89, 1, 89, 1, 90, 1, 90, 1, 90, 1, 90, 1, 90, 1, 91, 1, 91, 1, 91, 1, 91, 1, 91, 1, 92, 1, 92, 1, 92, 1, 92, 1, 93, 1, 93, 1, 93, 1, 93, 1, 94, 1, 94, 1, 94, 1, 94, 1, 95, 1, 95, 1, 95, 1, 95, 1, 95, 1, 96, 1, 96, 1, 96, 1, 96, 1, 97, 1, 97, 1, 97, 1, 97, 1, 98, 1, 98, 1, 98, 1, 98, 1, 99, 1, 99, 1, 99, 1, 99, 1, 100, 1, 100, 1, 100, 1, 100, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 102, 1, 102, 1, 102, 3, 102, 1084, 8, 102, 1, 103, 4, 103, 1087, 8, 103, 11, 103, 12, 103, 1088, 1, 104, 1, 104, 1, 104, 1, 104, 1, 105, 1, 105, 1, 105, 1, 105, 1, 106, 1, 106, 1, 106, 1, 106, 1, 107, 1, 107, 1, 107, 1, 107, 1, 108, 1, 108, 1, 108, 1, 108, 1, 109, 1, 109, 1, 109, 1, 109, 1, 109, 1, 110, 1, 110, 1, 110, 1, 110, 1, 111, 1, 111, 1, 111, 1, 111, 1, 112, 1, 112, 1, 112, 1, 112, 1, 112, 1, 113, 1, 113, 1, 113, 1, 113, 1, 113, 1, 114, 1, 114, 1, 114, 1, 114, 3, 114, 1138, 8, 114, 1, 115, 1, 115, 3, 115, 1142, 8, 115, 1, 115, 5, 115, 1145, 8, 115, 10, 115, 12, 115, 1148, 9, 115, 1, 115, 1, 115, 3, 115, 1152, 8, 115, 1, 115, 4, 115, 1155, 8, 115, 11, 115, 12, 115, 1156, 3, 115, 1159, 8, 115, 1, 116, 1, 116, 4, 116, 1163, 8, 116, 11, 116, 12, 116, 1164, 1, 117, 1, 117, 1, 117, 1, 117, 1, 118, 1, 118, 1, 118, 1, 118, 1, 119, 1, 119, 1, 119, 1, 119, 1, 120, 1, 120, 1, 120, 1, 120, 1, 120, 1, 121, 1, 121, 1, 121, 1, 121, 1, 122, 1, 122, 1, 122, 1, 122, 1, 123, 1, 123, 1, 123, 1, 123, 1, 124, 1, 124, 1, 124, 1, 124, 1, 124, 1, 125, 1, 125, 1, 125, 1, 125, 1, 125, 1, 126, 1, 126, 1, 126, 1, 127, 1, 127, 1, 127, 1, 127, 1, 128, 1, 128, 1, 128, 1, 128, 1, 129, 1, 129, 1, 129, 1, 129, 1, 130, 1, 130, 1, 130, 1, 130, 1, 131, 1, 131, 1, 131, 1, 131, 1, 131, 1, 132, 1, 132, 1, 132, 1, 132, 1, 132, 1, 133, 1, 133, 1, 133, 1, 133, 1, 133, 1, 134, 1, 134, 1, 134, 1, 134, 1, 134, 1, 134, 1, 134, 1, 135, 1, 135, 1, 136, 4, 136, 1250, 8, 136, 11, 136, 12, 136, 1251, 1, 136, 1, 136, 3, 136, 1256, 8, 136, 1, 136, 4, 136, 1259, 8, 136, 11, 136, 12, 136, 1260, 1, 137, 1, 137, 1, 137, 1, 137, 1, 138, 1, 138, 1, 138, 1, 138, 1, 139, 1, 139, 1, 139, 1, 139, 1, 140, 1, 140, 1, 140, 1, 140, 1, 141, 1, 141, 1, 141, 1, 141, 1, 141, 1, 141, 1, 142, 1, 142, 1, 142, 1, 142, 1, 143, 1, 143, 1, 143, 1, 143, 1, 144, 1, 144, 1, 144, 1, 144, 1, 145, 1, 145, 1, 145, 1, 145, 1, 146, 1, 146, 1, 146, 1, 146, 1, 147, 1, 147, 1, 147, 1, 147, 1, 148, 1, 148, 1, 148, 1, 148, 1, 148, 1, 149, 1, 149, 1, 149, 1, 149, 1, 149, 1, 150, 1, 150, 1, 150, 1, 150, 1, 151, 1, 151, 1, 151, 1, 151, 1, 152, 1, 152, 1, 152, 1, 152, 1, 153, 1, 153, 1, 153, 1, 153, 1, 153, 1, 154, 1, 154, 1, 154, 1, 154, 1, 155, 1, 155, 1, 155, 1, 155, 1, 155, 1, 156, 1, 156, 1, 156, 1, 156, 1, 156, 1, 157, 1, 157, 1, 157, 1, 157, 1, 158, 1, 158, 1, 158, 1, 158, 1, 159, 1, 159, 1, 159, 1, 159, 1, 160, 1, 160, 1, 160, 1, 160, 1, 161, 1, 161, 1, 161, 1, 161, 1, 162, 1, 162, 1, 162, 1, 162, 1, 162, 1, 163, 1, 163, 1, 163, 1, 163, 1, 163, 1, 164, 1, 164, 1, 164, 1, 164, 1, 165, 1, 165, 1, 165, 1, 165, 1, 166, 1, 166, 1, 166, 1, 166, 1, 167, 1, 167, 1, 167, 1, 167, 1, 167, 1, 168, 1, 168, 1, 168, 1, 168, 1, 169, 1, 169, 1, 169, 1, 169, 1, 169, 4, 169, 1406, 8, 169, 11, 169, 12, 169, 1407, 1, 170, 1, 170, 1, 170, 1, 170, 1, 171, 1, 171, 1, 171, 1, 171, 1, 172, 1, 172, 1, 172, 1, 172, 1, 173, 1, 173, 1, 173, 1, 173, 1, 173, 1, 174, 1, 174, 1, 174, 1, 174, 1, 175, 1, 175, 1, 175, 1, 175, 1, 176, 1, 176, 1, 176, 1, 176, 1, 177, 1, 177, 1, 177, 1, 177, 1, 177, 1, 178, 1, 178, 1, 178, 1, 178, 1, 179, 1, 179, 1, 179, 1, 179, 1, 180, 1, 180, 1, 180, 1, 180, 1, 181, 1, 181, 1, 181, 1, 181, 1, 182, 1, 182, 1, 182, 1, 182, 1, 183, 1, 183, 1, 183, 1, 183, 1, 183, 1, 183, 1, 184, 1, 184, 1, 184, 1, 184, 1, 185, 1, 185, 1, 185, 1, 185, 1, 186, 1, 186, 1, 186, 1, 186, 1, 187, 1, 187, 1, 187, 1, 187, 1, 188, 1, 188, 1, 188, 1, 188, 1, 189, 1, 189, 1, 189, 1, 189, 1, 190, 1, 190, 1, 190, 1, 190, 1, 190, 1, 191, 1, 191, 1, 191, 1, 191, 1, 192, 1, 192, 1, 192, 1, 192, 1, 193, 1, 193, 1, 193, 1, 193, 1, 193, 1, 193, 1, 194, 1, 194, 1, 194, 1, 194, 1, 194, 1, 194, 1, 194, 1, 194, 1, 194, 1, 195, 1, 195, 1, 195, 1, 195, 1, 196, 1, 196, 1, 196, 1, 196, 1, 197, 1, 197, 1, 197, 1, 197, 1, 198, 1, 198, 1, 198, 1, 198, 1, 199, 1, 199, 1, 199, 1, 199, 1, 200, 1, 200, 1, 200, 1, 200, 1, 201, 1, 201, 1, 201, 1, 201, 1, 202, 1, 202, 1, 202, 1, 202, 1, 203, 1, 203, 1, 203, 1, 203, 1, 203, 1, 204, 1, 204, 1, 204, 1, 204, 1, 204, 1, 204, 1, 205, 1, 205, 1, 205, 1, 205, 1, 205, 1, 205, 1, 206, 1, 206, 1, 206, 1, 206, 1, 207, 1, 207, 1, 207, 1, 207, 1, 208, 1, 208, 1, 208, 1, 208, 1, 209, 1, 209, 1, 209, 1, 209, 1, 209, 1, 209, 1, 210, 1, 210, 1, 210, 1, 210, 1, 210, 1, 210, 1, 211, 1, 211, 1, 211, 1, 211, 1, 212, 1, 212, 1, 212, 1, 212, 1, 213, 1, 213, 1, 213, 1, 213, 1, 214, 1, 214, 1, 214, 1, 214, 1, 214, 1, 214, 1, 215, 1, 215, 1, 215, 1, 215, 1, 215, 1, 215, 1, 216, 1, 216, 1, 216, 1, 216, 1, 216, 1, 216, 1, 217, 1, 217, 1, 217, 1, 217, 1, 217, 2, 693, 762, 0, 218, 16, 1, 18, 2, 20, 3, 22, 4, 24, 5, 26, 6, 28, 7, 30, 8, 32, 9, 34, 10, 36, 11, 38, 12, 40, 13, 42, 14, 44, 15, 46, 16, 48, 17, 50, 18, 52, 19, 54, 20, 56, 21, 58, 22, 60, 23, 62, 24, 64, 25, 66, 26, 68, 27, 70, 28, 72, 29, 74, 0, 76, 0, 78, 0, 80, 0, 82, 0, 84, 0, 86, 0, 88, 0, 90, 0, 92, 0, 94, 30, 96, 31, 98, 32, 100, 33, 102, 34, 104, 35, 106, 36, 108, 37, 110, 38, 112, 39, 114, 40, 116, 41, 118, 42, 120, 43, 122, 44, 124, 45, 126, 46, 128, 47, 130, 48, 132, 49, 134, 50, 136, 51, 138, 52, 140, 53, 142, 54, 144, 55, 146, 56, 148, 57, 150, 58, 152, 59, 154, 60, 156, 61, 158, 62, 160, 63, 162, 64, 164, 65, 166, 66, 168, 67, 170, 68, 172, 69, 174, 70, 176, 0, 178, 71, 180, 72, 182, 73, 184, 74, 186, 0, 188, 75, 190, 76, 192, 77, 194, 78, 196, 0, 198, 0, 200, 79, 202, 80, 204, 81, 206, 0, 208, 0, 210, 0, 212, 0, 214, 0, 216, 0, 218, 82, 220, 0, 222, 83, 224, 0, 226, 0, 228, 84, 230, 85, 232, 86, 234, 0, 236, 0, 238, 0, 240, 0, 242, 0, 244, 0, 246, 0, 248, 87, 250, 88, 252, 89, 254, 90, 256, 0, 258, 0, 260, 0, 262, 0, 264, 0, 266, 0, 268, 91, 270, 0, 272, 92, 274, 93, 276, 94, 278, 0, 280, 0, 282, 95, 284, 96, 286, 0, 288, 97, 290, 0, 292, 98, 294, 99, 296, 100, 298, 0, 300, 0, 302, 0, 304, 0, 306, 0, 308, 0, 310, 0, 312, 0, 314, 0, 316, 101, 318, 102, 320, 103, 322, 0, 324, 0, 326, 0, 328, 0, 330, 0, 332, 0, 334, 104, 336, 105, 338, 106, 340, 0, 342, 107, 344, 108, 346, 109, 348, 110, 350, 0, 352, 0, 354, 111, 356, 112, 358, 113, 360, 114, 362, 0, 364, 0, 366, 0, 368, 0, 370, 0, 372, 0, 374, 0, 376, 115, 378, 116, 380, 117, 382, 0, 384, 0, 386, 0, 388, 0, 390, 118, 392, 119, 394, 120, 396, 0, 398, 0, 400, 0, 402, 0, 404, 121, 406, 0, 408, 0, 410, 0, 412, 0, 414, 0, 416, 122, 418, 123, 420, 124, 422, 0, 424, 0, 426, 0, 428, 125, 430, 126, 432, 127, 434, 0, 436, 0, 438, 128, 440, 129, 442, 130, 444, 0, 446, 0, 448, 0, 450, 0, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 36, 2, 0, 68, 68, 100, 100, 2, 0, 73, 73, 105, 105, 2, 0, 83, 83, 115, 115, 2, 0, 69, 69, 101, 101, 2, 0, 67, 67, 99, 99, 2, 0, 84, 84, 116, 116, 2, 0, 82, 82, 114, 114, 2, 0, 79, 79, 111, 111, 2, 0, 80, 80, 112, 112, 2, 0, 78, 78, 110, 110, 2, 0, 72, 72, 104, 104, 2, 0, 86, 86, 118, 118, 2, 0, 65, 65, 97, 97, 2, 0, 76, 76, 108, 108, 2, 0, 88, 88, 120, 120, 2, 0, 70, 70, 102, 102, 2, 0, 77, 77, 109, 109, 2, 0, 71, 71, 103, 103, 2, 0, 75, 75, 107, 107, 2, 0, 87, 87, 119, 119, 2, 0, 85, 85, 117, 117, 2, 0, 74, 74, 106, 106, 6, 0, 9, 10, 13, 13, 32, 32, 47, 47, 91, 91, 93, 93, 2, 0, 10, 10, 13, 13, 3, 0, 9, 10, 13, 13, 32, 32, 1, 0, 48, 57, 2, 0, 65, 90, 97, 122, 8, 0, 34, 34, 78, 78, 82, 82, 84, 84, 92, 92, 110, 110, 114, 114, 116, 116, 4, 0, 10, 10, 13, 13, 34, 34, 92, 92, 2, 0, 43, 43, 45, 45, 1, 0, 96, 96, 2, 0, 66, 66, 98, 98, 2, 0, 89, 89, 121, 121, 11, 0, 9, 10, 13, 13, 32, 32, 34, 34, 44, 44, 47, 47, 58, 58, 61, 61, 91, 91, 93, 93, 124, 124, 2, 0, 42, 42, 47, 47, 11, 0, 9, 10, 13, 13, 32, 32, 34, 35, 44, 44, 47, 47, 58, 58, 60, 60, 62, 63, 92, 92, 124, 124, 1656, 0, 16, 1, 0, 0, 0, 0, 18, 1, 0, 0, 0, 0, 20, 1, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 24, 1, 0, 0, 0, 0, 26, 1, 0, 0, 0, 0, 28, 1, 0, 0, 0, 0, 30, 1, 0, 0, 0, 0, 32, 1, 0, 0, 0, 0, 34, 1, 0, 0, 0, 0, 36, 1, 0, 0, 0, 0, 38, 1, 0, 0, 0, 0, 40, 1, 0, 0, 0, 0, 42, 1, 0, 0, 0, 0, 44, 1, 0, 0, 0, 0, 46, 1, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 50, 1, 0, 0, 0, 0, 52, 1, 0, 0, 0, 0, 54, 1, 0, 0, 0, 0, 56, 1, 0, 0, 0, 0, 58, 1, 0, 0, 0, 0, 60, 1, 0, 0, 0, 0, 62, 1, 0, 0, 0, 0, 64, 1, 0, 0, 0, 0, 66, 1, 0, 0, 0, 0, 68, 1, 0, 0, 0, 0, 70, 1, 0, 0, 0, 1, 72, 1, 0, 0, 0, 1, 94, 1, 0, 0, 0, 1, 96, 1, 0, 0, 0, 1, 98, 1, 0, 0, 0, 1, 100, 1, 0, 0, 0, 1, 102, 1, 0, 0, 0, 1, 104, 1, 0, 0, 0, 1, 106, 1, 0, 0, 0, 1, 108, 1, 0, 0, 0, 1, 110, 1, 0, 0, 0, 1, 112, 1, 0, 0, 0, 1, 114, 1, 0, 0, 0, 1, 116, 1, 0, 0, 0, 1, 118, 1, 0, 0, 0, 1, 120, 1, 0, 0, 0, 1, 122, 1, 0, 0, 0, 1, 124, 1, 0, 0, 0, 1, 126, 1, 0, 0, 0, 1, 128, 1, 0, 0, 0, 1, 130, 1, 0, 0, 0, 1, 132, 1, 0, 0, 0, 1, 134, 1, 0, 0, 0, 1, 136, 1, 0, 0, 0, 1, 138, 1, 0, 0, 0, 1, 140, 1, 0, 0, 0, 1, 142, 1, 0, 0, 0, 1, 144, 1, 0, 0, 0, 1, 146, 1, 0, 0, 0, 1, 148, 1, 0, 0, 0, 1, 150, 1, 0, 0, 0, 1, 152, 1, 0, 0, 0, 1, 154, 1, 0, 0, 0, 1, 156, 1, 0, 0, 0, 1, 158, 1, 0, 0, 0, 1, 160, 1, 0, 0, 0, 1, 162, 1, 0, 0, 0, 1, 164, 1, 0, 0, 0, 1, 166, 1, 0, 0, 0, 1, 168, 1, 0, 0, 0, 1, 170, 1, 0, 0, 0, 1, 172, 1, 0, 0, 0, 1, 174, 1, 0, 0, 0, 1, 176, 1, 0, 0, 0, 1, 178, 1, 0, 0, 0, 1, 180, 1, 0, 0, 0, 1, 182, 1, 0, 0, 0, 1, 184, 1, 0, 0, 0, 1, 188, 1, 0, 0, 0, 1, 190, 1, 0, 0, 0, 1, 192, 1, 0, 0, 0, 1, 194, 1, 0, 0, 0, 2, 196, 1, 0, 0, 0, 2, 198, 1, 0, 0, 0, 2, 200, 1, 0, 0, 0, 2, 202, 1, 0, 0, 0, 2, 204, 1, 0, 0, 0, 3, 206, 1, 0, 0, 0, 3, 208, 1, 0, 0, 0, 3, 210, 1, 0, 0, 0, 3, 212, 1, 0, 0, 0, 3, 214, 1, 0, 0, 0, 3, 216, 1, 0, 0, 0, 3, 218, 1, 0, 0, 0, 3, 222, 1, 0, 0, 0, 3, 224, 1, 0, 0, 0, 3, 226, 1, 0, 0, 0, 3, 228, 1, 0, 0, 0, 3, 230, 1, 0, 0, 0, 3, 232, 1, 0, 0, 0, 4, 234, 1, 0, 0, 0, 4, 236, 1, 0, 0, 0, 4, 238, 1, 0, 0, 0, 4, 240, 1, 0, 0, 0, 4, 242, 1, 0, 0, 0, 4, 248, 1, 0, 0, 0, 4, 250, 1, 0, 0, 0, 4, 252, 1, 0, 0, 0, 4, 254, 1, 0, 0, 0, 5, 256, 1, 0, 0, 0, 5, 258, 1, 0, 0, 0, 5, 260, 1, 0, 0, 0, 5, 262, 1, 0, 0, 0, 5, 264, 1, 0, 0, 0, 5, 266, 1, 0, 0, 0, 5, 268, 1, 0, 0, 0, 5, 270, 1, 0, 0, 0, 5, 272, 1, 0, 0, 0, 5, 274, 1, 0, 0, 0, 5, 276, 1, 0, 0, 0, 6, 278, 1, 0, 0, 0, 6, 280, 1, 0, 0, 0, 6, 282, 1, 0, 0, 0, 6, 284, 1, 0, 0, 0, 6, 288, 1, 0, 0, 0, 6, 290, 1, 0, 0, 0, 6, 292, 1, 0, 0, 0, 6, 294, 1, 0, 0, 0, 6, 296, 1, 0, 0, 0, 7, 298, 1, 0, 0, 0, 7, 300, 1, 0, 0, 0, 7, 302, 1, 0, 0, 0, 7, 304, 1, 0, 0, 0, 7, 306, 1, 0, 0, 0, 7, 308, 1, 0, 0, 0, 7, 310, 1, 0, 0, 0, 7, 312, 1, 0, 0, 0, 7, 314, 1, 0, 0, 0, 7, 316, 1, 0, 0, 0, 7, 318, 1, 0, 0, 0, 7, 320, 1, 0, 0, 0, 8, 322, 1, 0, 0, 0, 8, 324, 1, 0, 0, 0, 8, 326, 1, 0, 0, 0, 8, 328, 1, 0, 0, 0, 8, 330, 1, 0, 0, 0, 8, 332, 1, 0, 0, 0, 8, 334, 1, 0, 0, 0, 8, 336, 1, 0, 0, 0, 8, 338, 1, 0, 0, 0, 9, 340, 1, 0, 0, 0, 9, 342, 1, 0, 0, 0, 9, 344, 1, 0, 0, 0, 9, 346, 1, 0, 0, 0, 9, 348, 1, 0, 0, 0, 10, 350, 1, 0, 0, 0, 10, 352, 1, 0, 0, 0, 10, 354, 1, 0, 0, 0, 10, 356, 1, 0, 0, 0, 10, 358, 1, 0, 0, 0, 10, 360, 1, 0, 0, 0, 11, 362, 1, 0, 0, 0, 11, 364, 1, 0, 0, 0, 11, 366, 1, 0, 0, 0, 11, 368, 1, 0, 0, 0, 11, 370, 1, 0, 0, 0, 11, 372, 1, 0, 0, 0, 11, 374, 1, 0, 0, 0, 11, 376, 1, 0, 0, 0, 11, 378, 1, 0, 0, 0, 11, 380, 1, 0, 0, 0, 12, 382, 1, 0, 0, 0, 12, 384, 1, 0, 0, 0, 12, 386, 1, 0, 0, 0, 12, 388, 1, 0, 0, 0, 12, 390, 1, 0, 0, 0, 12, 392, 1, 0, 0, 0, 12, 394, 1, 0, 0, 0, 13, 396, 1, 0, 0, 0, 13, 398, 1, 0, 0, 0, 13, 400, 1, 0, 0, 0, 13, 402, 1, 0, 0, 0, 13, 404, 1, 0, 0, 0, 13, 406, 1, 0, 0, 0, 13, 408, 1, 0, 0, 0, 13, 410, 1, 0, 0, 0, 13, 412, 1, 0, 0, 0, 13, 414, 1, 0, 0, 0, 13, 416, 1, 0, 0, 0, 13, 418, 1, 0, 0, 0, 13, 420, 1, 0, 0, 0, 14, 422, 1, 0, 0, 0, 14, 424, 1, 0, 0, 0, 14, 426, 1, 0, 0, 0, 14, 428, 1, 0, 0, 0, 14, 430, 1, 0, 0, 0, 14, 432, 1, 0, 0, 0, 15, 434, 1, 0, 0, 0, 15, 436, 1, 0, 0, 0, 15, 438, 1, 0, 0, 0, 15, 440, 1, 0, 0, 0, 15, 442, 1, 0, 0, 0, 15, 444, 1, 0, 0, 0, 15, 446, 1, 0, 0, 0, 15, 448, 1, 0, 0, 0, 15, 450, 1, 0, 0, 0, 16, 452, 1, 0, 0, 0, 18, 462, 1, 0, 0, 0, 20, 469, 1, 0, 0, 0, 22, 478, 1, 0, 0, 0, 24, 485, 1, 0, 0, 0, 26, 495, 1, 0, 0, 0, 28, 502, 1, 0, 0, 0, 30, 509, 1, 0, 0, 0, 32, 516, 1, 0, 0, 0, 34, 524, 1, 0, 0, 0, 36, 536, 1, 0, 0, 0, 38, 545, 1, 0, 0, 0, 40, 551, 1, 0, 0, 0, 42, 558, 1, 0, 0, 0, 44, 565, 1, 0, 0, 0, 46, 573, 1, 0, 0, 0, 48, 581, 1, 0, 0, 0, 50, 596, 1, 0, 0, 0, 52, 608, 1, 0, 0, 0, 54, 619, 1, 0, 0, 0, 56, 627, 1, 0, 0, 0, 58, 635, 1, 0, 0, 0, 60, 643, 1, 0, 0, 0, 62, 652, 1, 0, 0, 0, 64, 663, 1, 0, 0, 0, 66, 669, 1, 0, 0, 0, 68, 686, 1, 0, 0, 0, 70, 702, 1, 0, 0, 0, 72, 708, 1, 0, 0, 0, 74, 712, 1, 0, 0, 0, 76, 714, 1, 0, 0, 0, 78, 716, 1, 0, 0, 0, 80, 719, 1, 0, 0, 0, 82, 721, 1, 0, 0, 0, 84, 730, 1, 0, 0, 0, 86, 732, 1, 0, 0, 0, 88, 737, 1, 0, 0, 0, 90, 739, 1, 0, 0, 0, 92, 744, 1, 0, 0, 0, 94, 775, 1, 0, 0, 0, 96, 778, 1, 0, 0, 0, 98, 824, 1, 0, 0, 0, 100, 826, 1, 0, 0, 0, 102, 829, 1, 0, 0, 0, 104, 833, 1, 0, 0, 0, 106, 837, 1, 0, 0, 0, 108, 839, 1, 0, 0, 0, 110, 842, 1, 0, 0, 0, 112, 844, 1, 0, 0, 0, 114, 846, 1, 0, 0, 0, 116, 851, 1, 0, 0, 0, 118, 853, 1, 0, 0, 0, 120, 859, 1, 0, 0, 0, 122, 865, 1, 0, 0, 0, 124, 868, 1, 0, 0, 0, 126, 871, 1, 0, 0, 0, 128, 876, 1, 0, 0, 0, 130, 881, 1, 0, 0, 0, 132, 883, 1, 0, 0, 0, 134, 887, 1, 0, 0, 0, 136, 892, 1, 0, 0, 0, 138, 898, 1, 0, 0, 0, 140, 901, 1, 0, 0, 0, 142, 903, 1, 0, 0, 0, 144, 909, 1, 0, 0, 0, 146, 911, 1, 0, 0, 0, 148, 916, 1, 0, 0, 0, 150, 919, 1, 0, 0, 0, 152, 922, 1, 0, 0, 0, 154, 925, 1, 0, 0, 0, 156, 927, 1, 0, 0, 0, 158, 930, 1, 0, 0, 0, 160, 932, 1, 0, 0, 0, 162, 935, 1, 0, 0, 0, 164, 937, 1, 0, 0, 0, 166, 939, 1, 0, 0, 0, 168, 941, 1, 0, 0, 0, 170, 943, 1, 0, 0, 0, 172, 945, 1, 0, 0, 0, 174, 948, 1, 0, 0, 0, 176, 951, 1, 0, 0, 0, 178, 972, 1, 0, 0, 0, 180, 974, 1, 0, 0, 0, 182, 979, 1, 0, 0, 0, 184, 1000, 1, 0, 0, 0, 186, 1002, 1, 0, 0, 0, 188, 1010, 1, 0, 0, 0, 190, 1012, 1, 0, 0, 0, 192, 1016, 1, 0, 0, 0, 194, 1020, 1, 0, 0, 0, 196, 1024, 1, 0, 0, 0, 198, 1029, 1, 0, 0, 0, 200, 1034, 1, 0, 0, 0, 202, 1038, 1, 0, 0, 0, 204, 1042, 1, 0, 0, 0, 206, 1046, 1, 0, 0, 0, 208, 1051, 1, 0, 0, 0, 210, 1055, 1, 0, 0, 0, 212, 1059, 1, 0, 0, 0, 214, 1063, 1, 0, 0, 0, 216, 1067, 1, 0, 0, 0, 218, 1071, 1, 0, 0, 0, 220, 1083, 1, 0, 0, 0, 222, 1086, 1, 0, 0, 0, 224, 1090, 1, 0, 0, 0, 226, 1094, 1, 0, 0, 0, 228, 1098, 1, 0, 0, 0, 230, 1102, 1, 0, 0, 0, 232, 1106, 1, 0, 0, 0, 234, 1110, 1, 0, 0, 0, 236, 1115, 1, 0, 0, 0, 238, 1119, 1, 0, 0, 0, 240, 1123, 1, 0, 0, 0, 242, 1128, 1, 0, 0, 0, 244, 1137, 1, 0, 0, 0, 246, 1158, 1, 0, 0, 0, 248, 1162, 1, 0, 0, 0, 250, 1166, 1, 0, 0, 0, 252, 1170, 1, 0, 0, 0, 254, 1174, 1, 0, 0, 0, 256, 1178, 1, 0, 0, 0, 258, 1183, 1, 0, 0, 0, 260, 1187, 1, 0, 0, 0, 262, 1191, 1, 0, 0, 0, 264, 1195, 1, 0, 0, 0, 266, 1200, 1, 0, 0, 0, 268, 1205, 1, 0, 0, 0, 270, 1208, 1, 0, 0, 0, 272, 1212, 1, 0, 0, 0, 274, 1216, 1, 0, 0, 0, 276, 1220, 1, 0, 0, 0, 278, 1224, 1, 0, 0, 0, 280, 1229, 1, 0, 0, 0, 282, 1234, 1, 0, 0, 0, 284, 1239, 1, 0, 0, 0, 286, 1246, 1, 0, 0, 0, 288, 1255, 1, 0, 0, 0, 290, 1262, 1, 0, 0, 0, 292, 1266, 1, 0, 0, 0, 294, 1270, 1, 0, 0, 0, 296, 1274, 1, 0, 0, 0, 298, 1278, 1, 0, 0, 0, 300, 1284, 1, 0, 0, 0, 302, 1288, 1, 0, 0, 0, 304, 1292, 1, 0, 0, 0, 306, 1296, 1, 0, 0, 0, 308, 1300, 1, 0, 0, 0, 310, 1304, 1, 0, 0, 0, 312, 1308, 1, 0, 0, 0, 314, 1313, 1, 0, 0, 0, 316, 1318, 1, 0, 0, 0, 318, 1322, 1, 0, 0, 0, 320, 1326, 1, 0, 0, 0, 322, 1330, 1, 0, 0, 0, 324, 1335, 1, 0, 0, 0, 326, 1339, 1, 0, 0, 0, 328, 1344, 1, 0, 0, 0, 330, 1349, 1, 0, 0, 0, 332, 1353, 1, 0, 0, 0, 334, 1357, 1, 0, 0, 0, 336, 1361, 1, 0, 0, 0, 338, 1365, 1, 0, 0, 0, 340, 1369, 1, 0, 0, 0, 342, 1374, 1, 0, 0, 0, 344, 1379, 1, 0, 0, 0, 346, 1383, 1, 0, 0, 0, 348, 1387, 1, 0, 0, 0, 350, 1391, 1, 0, 0, 0, 352, 1396, 1, 0, 0, 0, 354, 1405, 1, 0, 0, 0, 356, 1409, 1, 0, 0, 0, 358, 1413, 1, 0, 0, 0, 360, 1417, 1, 0, 0, 0, 362, 1421, 1, 0, 0, 0, 364, 1426, 1, 0, 0, 0, 366, 1430, 1, 0, 0, 0, 368, 1434, 1, 0, 0, 0, 370, 1438, 1, 0, 0, 0, 372, 1443, 1, 0, 0, 0, 374, 1447, 1, 0, 0, 0, 376, 1451, 1, 0, 0, 0, 378, 1455, 1, 0, 0, 0, 380, 1459, 1, 0, 0, 0, 382, 1463, 1, 0, 0, 0, 384, 1469, 1, 0, 0, 0, 386, 1473, 1, 0, 0, 0, 388, 1477, 1, 0, 0, 0, 390, 1481, 1, 0, 0, 0, 392, 1485, 1, 0, 0, 0, 394, 1489, 1, 0, 0, 0, 396, 1493, 1, 0, 0, 0, 398, 1498, 1, 0, 0, 0, 400, 1502, 1, 0, 0, 0, 402, 1506, 1, 0, 0, 0, 404, 1512, 1, 0, 0, 0, 406, 1521, 1, 0, 0, 0, 408, 1525, 1, 0, 0, 0, 410, 1529, 1, 0, 0, 0, 412, 1533, 1, 0, 0, 0, 414, 1537, 1, 0, 0, 0, 416, 1541, 1, 0, 0, 0, 418, 1545, 1, 0, 0, 0, 420, 1549, 1, 0, 0, 0, 422, 1553, 1, 0, 0, 0, 424, 1558, 1, 0, 0, 0, 426, 1564, 1, 0, 0, 0, 428, 1570, 1, 0, 0, 0, 430, 1574, 1, 0, 0, 0, 432, 1578, 1, 0, 0, 0, 434, 1582, 1, 0, 0, 0, 436, 1588, 1, 0, 0, 0, 438, 1594, 1, 0, 0, 0, 440, 1598, 1, 0, 0, 0, 442, 1602, 1, 0, 0, 0, 444, 1606, 1, 0, 0, 0, 446, 1612, 1, 0, 0, 0, 448, 1618, 1, 0, 0, 0, 450, 1624, 1, 0, 0, 0, 452, 453, 7, 0, 0, 0, 453, 454, 7, 1, 0, 0, 454, 455, 7, 2, 0, 0, 455, 456, 7, 2, 0, 0, 456, 457, 7, 3, 0, 0, 457, 458, 7, 4, 0, 0, 458, 459, 7, 5, 0, 0, 459, 460, 1, 0, 0, 0, 460, 461, 6, 0, 0, 0, 461, 17, 1, 0, 0, 0, 462, 463, 7, 0, 0, 0, 463, 464, 7, 6, 0, 0, 464, 465, 7, 7, 0, 0, 465, 466, 7, 8, 0, 0, 466, 467, 1, 0, 0, 0, 467, 468, 6, 1, 1, 0, 468, 19, 1, 0, 0, 0, 469, 470, 7, 3, 0, 0, 470, 471, 7, 9, 0, 0, 471, 472, 7, 6, 0, 0, 472, 473, 7, 1, 0, 0, 473, 474, 7, 4, 0, 0, 474, 475, 7, 10, 0, 0, 475, 476, 1, 0, 0, 0, 476, 477, 6, 2, 2, 0, 477, 21, 1, 0, 0, 0, 478, 479, 7, 3, 0, 0, 479, 480, 7, 11, 0, 0, 480, 481, 7, 12, 0, 0, 481, 482, 7, 13, 0, 0, 482, 483, 1, 0, 0, 0, 483, 484, 6, 3, 0, 0, 484, 23, 1, 0, 0, 0, 485, 486, 7, 3, 0, 0, 486, 487, 7, 14, 0, 0, 487, 488, 7, 8, 0, 0, 488, 489, 7, 13, 0, 0, 489, 490, 7, 12, 0, 0, 490, 491, 7, 1, 0, 0, 491, 492, 7, 9, 0, 0, 492, 493, 1, 0, 0, 0, 493, 494, 6, 4, 3, 0, 494, 25, 1, 0, 0, 0, 495, 496, 7, 15, 0, 0, 496, 497, 7, 6, 0, 0, 497, 498, 7, 7, 0, 0, 498, 499, 7, 16, 0, 0, 499, 500, 1, 0, 0, 0, 500, 501, 6, 5, 4, 0, 501, 27, 1, 0, 0, 0, 502, 503, 7, 17, 0, 0, 503, 504, 7, 6, 0, 0, 504, 505, 7, 7, 0, 0, 505, 506, 7, 18, 0, 0, 506, 507, 1, 0, 0, 0, 507, 508, 6, 6, 0, 0, 508, 29, 1, 0, 0, 0, 509, 510, 7, 18, 0, 0, 510, 511, 7, 3, 0, 0, 511, 512, 7, 3, 0, 0, 512, 513, 7, 8, 0, 0, 513, 514, 1, 0, 0, 0, 514, 515, 6, 7, 1, 0, 515, 31, 1, 0, 0, 0, 516, 517, 7, 13, 0, 0, 517, 518, 7, 1, 0, 0, 518, 519, 7, 16, 0, 0, 519, 520, 7, 1, 0, 0, 520, 521, 7, 5, 0, 0, 521, 522, 1, 0, 0, 0, 522, 523, 6, 8, 0, 0, 523, 33, 1, 0, 0, 0, 524, 525, 7, 16, 0, 0, 525, 526, 7, 11, 0, 0, 526, 527, 5, 95, 0, 0, 527, 528, 7, 3, 0, 0, 528, 529, 7, 14, 0, 0, 529, 530, 7, 8, 0, 0, 530, 531, 7, 12, 0, 0, 531, 532, 7, 9, 0, 0, 532, 533, 7, 0, 0, 0, 533, 534, 1, 0, 0, 0, 534, 535, 6, 9, 5, 0, 535, 35, 1, 0, 0, 0, 536, 537, 7, 6, 0, 0, 537, 538, 7, 3, 0, 0, 538, 539, 7, 9, 0, 0, 539, 540, 7, 12, 0, 0, 540, 541, 7, 16, 0, 0, 541, 542, 7, 3, 0, 0, 542, 543, 1, 0, 0, 0, 543, 544, 6, 10, 6, 0, 544, 37, 1, 0, 0, 0, 545, 546, 7, 6, 0, 0, 546, 547, 7, 7, 0, 0, 547, 548, 7, 19, 0, 0, 548, 549, 1, 0, 0, 0, 549, 550, 6, 11, 0, 0, 550, 39, 1, 0, 0, 0, 551, 552, 7, 2, 0, 0, 552, 553, 7, 10, 0, 0, 553, 554, 7, 7, 0, 0, 554, 555, 7, 19, 0, 0, 555, 556, 1, 0, 0, 0, 556, 557, 6, 12, 7, 0, 557, 41, 1, 0, 0, 0, 558, 559, 7, 2, 0, 0, 559, 560, 7, 7, 0, 0, 560, 561, 7, 6, 0, 0, 561, 562, 7, 5, 0, 0, 562, 563, 1, 0, 0, 0, 563, 564, 6, 13, 0, 0, 564, 43, 1, 0, 0, 0, 565, 566, 7, 2, 0, 0, 566, 567, 7, 5, 0, 0, 567, 568, 7, 12, 0, 0, 568, 569, 7, 5, 0, 0, 569, 570, 7, 2, 0, 0, 570, 571, 1, 0, 0, 0, 571, 572, 6, 14, 0, 0, 572, 45, 1, 0, 0, 0, 573, 574, 7, 19, 0, 0, 574, 575, 7, 10, 0, 0, 575, 576, 7, 3, 0, 0, 576, 577, 7, 6, 0, 0, 577, 578, 7, 3, 0, 0, 578, 579, 1, 0, 0, 0, 579, 580, 6, 15, 0, 0, 580, 47, 1, 0, 0, 0, 581, 582, 4, 16, 0, 0, 582, 583, 7, 1, 0, 0, 583, 584, 7, 9, 0, 0, 584, 585, 7, 13, 0, 0, 585, 586, 7, 1, 0, 0, 586, 587, 7, 9, 0, 0, 587, 588, 7, 3, 0, 0, 588, 589, 7, 2, 0, 0, 589, 590, 7, 5, 0, 0, 590, 591, 7, 12, 0, 0, 591, 592, 7, 5, 0, 0, 592, 593, 7, 2, 0, 0, 593, 594, 1, 0, 0, 0, 594, 595, 6, 16, 0, 0, 595, 49, 1, 0, 0, 0, 596, 597, 4, 17, 1, 0, 597, 598, 7, 13, 0, 0, 598, 599, 7, 7, 0, 0, 599, 600, 7, 7, 0, 0, 600, 601, 7, 18, 0, 0, 601, 602, 7, 20, 0, 0, 602, 603, 7, 8, 0, 0, 603, 604, 5, 95, 0, 0, 604, 605, 5, 128020, 0, 0, 605, 606, 1, 0, 0, 0, 606, 607, 6, 17, 8, 0, 607, 51, 1, 0, 0, 0, 608, 609, 4, 18, 2, 0, 609, 610, 7, 16, 0, 0, 610, 611, 7, 3, 0, 0, 611, 612, 7, 5, 0, 0, 612, 613, 7, 6, 0, 0, 613, 614, 7, 1, 0, 0, 614, 615, 7, 4, 0, 0, 615, 616, 7, 2, 0, 0, 616, 617, 1, 0, 0, 0, 617, 618, 6, 18, 9, 0, 618, 53, 1, 0, 0, 0, 619, 620, 4, 19, 3, 0, 620, 621, 7, 21, 0, 0, 621, 622, 7, 7, 0, 0, 622, 623, 7, 1, 0, 0, 623, 624, 7, 9, 0, 0, 624, 625, 1, 0, 0, 0, 625, 626, 6, 19, 10, 0, 626, 55, 1, 0, 0, 0, 627, 628, 4, 20, 4, 0, 628, 629, 7, 15, 0, 0, 629, 630, 7, 20, 0, 0, 630, 631, 7, 13, 0, 0, 631, 632, 7, 13, 0, 0, 632, 633, 1, 0, 0, 0, 633, 634, 6, 20, 10, 0, 634, 57, 1, 0, 0, 0, 635, 636, 4, 21, 5, 0, 636, 637, 7, 13, 0, 0, 637, 638, 7, 3, 0, 0, 638, 639, 7, 15, 0, 0, 639, 640, 7, 5, 0, 0, 640, 641, 1, 0, 0, 0, 641, 642, 6, 21, 10, 0, 642, 59, 1, 0, 0, 0, 643, 644, 4, 22, 6, 0, 644, 645, 7, 6, 0, 0, 645, 646, 7, 1, 0, 0, 646, 647, 7, 17, 0, 0, 647, 648, 7, 10, 0, 0, 648, 649, 7, 5, 0, 0, 649, 650, 1, 0, 0, 0, 650, 651, 6, 22, 10, 0, 651, 61, 1, 0, 0, 0, 652, 653, 4, 23, 7, 0, 653, 654, 7, 13, 0, 0, 654, 655, 7, 7, 0, 0, 655, 656, 7, 7, 0, 0, 656, 657, 7, 18, 0, 0, 657, 658, 7, 20, 0, 0, 658, 659, 7, 8, 0, 0, 659, 660, 1, 0, 0, 0, 660, 661, 6, 23, 10, 0, 661, 63, 1, 0, 0, 0, 662, 664, 8, 22, 0, 0, 663, 662, 1, 0, 0, 0, 664, 665, 1, 0, 0, 0, 665, 663, 1, 0, 0, 0, 665, 666, 1, 0, 0, 0, 666, 667, 1, 0, 0, 0, 667, 668, 6, 24, 0, 0, 668, 65, 1, 0, 0, 0, 669, 670, 5, 47, 0, 0, 670, 671, 5, 47, 0, 0, 671, 675, 1, 0, 0, 0, 672, 674, 8, 23, 0, 0, 673, 672, 1, 0, 0, 0, 674, 677, 1, 0, 0, 0, 675, 673, 1, 0, 0, 0, 675, 676, 1, 0, 0, 0, 676, 679, 1, 0, 0, 0, 677, 675, 1, 0, 0, 0, 678, 680, 5, 13, 0, 0, 679, 678, 1, 0, 0, 0, 679, 680, 1, 0, 0, 0, 680, 682, 1, 0, 0, 0, 681, 683, 5, 10, 0, 0, 682, 681, 1, 0, 0, 0, 682, 683, 1, 0, 0, 0, 683, 684, 1, 0, 0, 0, 684, 685, 6, 25, 11, 0, 685, 67, 1, 0, 0, 0, 686, 687, 5, 47, 0, 0, 687, 688, 5, 42, 0, 0, 688, 693, 1, 0, 0, 0, 689, 692, 3, 68, 26, 0, 690, 692, 9, 0, 0, 0, 691, 689, 1, 0, 0, 0, 691, 690, 1, 0, 0, 0, 692, 695, 1, 0, 0, 0, 693, 694, 1, 0, 0, 0, 693, 691, 1, 0, 0, 0, 694, 696, 1, 0, 0, 0, 695, 693, 1, 0, 0, 0, 696, 697, 5, 42, 0, 0, 697, 698, 5, 47, 0, 0, 698, 699, 1, 0, 0, 0, 699, 700, 6, 26, 11, 0, 700, 69, 1, 0, 0, 0, 701, 703, 7, 24, 0, 0, 702, 701, 1, 0, 0, 0, 703, 704, 1, 0, 0, 0, 704, 702, 1, 0, 0, 0, 704, 705, 1, 0, 0, 0, 705, 706, 1, 0, 0, 0, 706, 707, 6, 27, 11, 0, 707, 71, 1, 0, 0, 0, 708, 709, 5, 124, 0, 0, 709, 710, 1, 0, 0, 0, 710, 711, 6, 28, 12, 0, 711, 73, 1, 0, 0, 0, 712, 713, 7, 25, 0, 0, 713, 75, 1, 0, 0, 0, 714, 715, 7, 26, 0, 0, 715, 77, 1, 0, 0, 0, 716, 717, 5, 92, 0, 0, 717, 718, 7, 27, 0, 0, 718, 79, 1, 0, 0, 0, 719, 720, 8, 28, 0, 0, 720, 81, 1, 0, 0, 0, 721, 723, 7, 3, 0, 0, 722, 724, 7, 29, 0, 0, 723, 722, 1, 0, 0, 0, 723, 724, 1, 0, 0, 0, 724, 726, 1, 0, 0, 0, 725, 727, 3, 74, 29, 0, 726, 725, 1, 0, 0, 0, 727, 728, 1, 0, 0, 0, 728, 726, 1, 0, 0, 0, 728, 729, 1, 0, 0, 0, 729, 83, 1, 0, 0, 0, 730, 731, 5, 64, 0, 0, 731, 85, 1, 0, 0, 0, 732, 733, 5, 96, 0, 0, 733, 87, 1, 0, 0, 0, 734, 738, 8, 30, 0, 0, 735, 736, 5, 96, 0, 0, 736, 738, 5, 96, 0, 0, 737, 734, 1, 0, 0, 0, 737, 735, 1, 0, 0, 0, 738, 89, 1, 0, 0, 0, 739, 740, 5, 95, 0, 0, 740, 91, 1, 0, 0, 0, 741, 745, 3, 76, 30, 0, 742, 745, 3, 74, 29, 0, 743, 745, 3, 90, 37, 0, 744, 741, 1, 0, 0, 0, 744, 742, 1, 0, 0, 0, 744, 743, 1, 0, 0, 0, 745, 93, 1, 0, 0, 0, 746, 751, 5, 34, 0, 0, 747, 750, 3, 78, 31, 0, 748, 750, 3, 80, 32, 0, 749, 747, 1, 0, 0, 0, 749, 748, 1, 0, 0, 0, 750, 753, 1, 0, 0, 0, 751, 749, 1, 0, 0, 0, 751, 752, 1, 0, 0, 0, 752, 754, 1, 0, 0, 0, 753, 751, 1, 0, 0, 0, 754, 776, 5, 34, 0, 0, 755, 756, 5, 34, 0, 0, 756, 757, 5, 34, 0, 0, 757, 758, 5, 34, 0, 0, 758, 762, 1, 0, 0, 0, 759, 761, 8, 23, 0, 0, 760, 759, 1, 0, 0, 0, 761, 764, 1, 0, 0, 0, 762, 763, 1, 0, 0, 0, 762, 760, 1, 0, 0, 0, 763, 765, 1, 0, 0, 0, 764, 762, 1, 0, 0, 0, 765, 766, 5, 34, 0, 0, 766, 767, 5, 34, 0, 0, 767, 768, 5, 34, 0, 0, 768, 770, 1, 0, 0, 0, 769, 771, 5, 34, 0, 0, 770, 769, 1, 0, 0, 0, 770, 771, 1, 0, 0, 0, 771, 773, 1, 0, 0, 0, 772, 774, 5, 34, 0, 0, 773, 772, 1, 0, 0, 0, 773, 774, 1, 0, 0, 0, 774, 776, 1, 0, 0, 0, 775, 746, 1, 0, 0, 0, 775, 755, 1, 0, 0, 0, 776, 95, 1, 0, 0, 0, 777, 779, 3, 74, 29, 0, 778, 777, 1, 0, 0, 0, 779, 780, 1, 0, 0, 0, 780, 778, 1, 0, 0, 0, 780, 781, 1, 0, 0, 0, 781, 97, 1, 0, 0, 0, 782, 784, 3, 74, 29, 0, 783, 782, 1, 0, 0, 0, 784, 785, 1, 0, 0, 0, 785, 783, 1, 0, 0, 0, 785, 786, 1, 0, 0, 0, 786, 787, 1, 0, 0, 0, 787, 791, 3, 116, 50, 0, 788, 790, 3, 74, 29, 0, 789, 788, 1, 0, 0, 0, 790, 793, 1, 0, 0, 0, 791, 789, 1, 0, 0, 0, 791, 792, 1, 0, 0, 0, 792, 825, 1, 0, 0, 0, 793, 791, 1, 0, 0, 0, 794, 796, 3, 116, 50, 0, 795, 797, 3, 74, 29, 0, 796, 795, 1, 0, 0, 0, 797, 798, 1, 0, 0, 0, 798, 796, 1, 0, 0, 0, 798, 799, 1, 0, 0, 0, 799, 825, 1, 0, 0, 0, 800, 802, 3, 74, 29, 0, 801, 800, 1, 0, 0, 0, 802, 803, 1, 0, 0, 0, 803, 801, 1, 0, 0, 0, 803, 804, 1, 0, 0, 0, 804, 812, 1, 0, 0, 0, 805, 809, 3, 116, 50, 0, 806, 808, 3, 74, 29, 0, 807, 806, 1, 0, 0, 0, 808, 811, 1, 0, 0, 0, 809, 807, 1, 0, 0, 0, 809, 810, 1, 0, 0, 0, 810, 813, 1, 0, 0, 0, 811, 809, 1, 0, 0, 0, 812, 805, 1, 0, 0, 0, 812, 813, 1, 0, 0, 0, 813, 814, 1, 0, 0, 0, 814, 815, 3, 82, 33, 0, 815, 825, 1, 0, 0, 0, 816, 818, 3, 116, 50, 0, 817, 819, 3, 74, 29, 0, 818, 817, 1, 0, 0, 0, 819, 820, 1, 0, 0, 0, 820, 818, 1, 0, 0, 0, 820, 821, 1, 0, 0, 0, 821, 822, 1, 0, 0, 0, 822, 823, 3, 82, 33, 0, 823, 825, 1, 0, 0, 0, 824, 783, 1, 0, 0, 0, 824, 794, 1, 0, 0, 0, 824, 801, 1, 0, 0, 0, 824, 816, 1, 0, 0, 0, 825, 99, 1, 0, 0, 0, 826, 827, 7, 31, 0, 0, 827, 828, 7, 32, 0, 0, 828, 101, 1, 0, 0, 0, 829, 830, 7, 12, 0, 0, 830, 831, 7, 9, 0, 0, 831, 832, 7, 0, 0, 0, 832, 103, 1, 0, 0, 0, 833, 834, 7, 12, 0, 0, 834, 835, 7, 2, 0, 0, 835, 836, 7, 4, 0, 0, 836, 105, 1, 0, 0, 0, 837, 838, 5, 61, 0, 0, 838, 107, 1, 0, 0, 0, 839, 840, 5, 58, 0, 0, 840, 841, 5, 58, 0, 0, 841, 109, 1, 0, 0, 0, 842, 843, 5, 58, 0, 0, 843, 111, 1, 0, 0, 0, 844, 845, 5, 44, 0, 0, 845, 113, 1, 0, 0, 0, 846, 847, 7, 0, 0, 0, 847, 848, 7, 3, 0, 0, 848, 849, 7, 2, 0, 0, 849, 850, 7, 4, 0, 0, 850, 115, 1, 0, 0, 0, 851, 852, 5, 46, 0, 0, 852, 117, 1, 0, 0, 0, 853, 854, 7, 15, 0, 0, 854, 855, 7, 12, 0, 0, 855, 856, 7, 13, 0, 0, 856, 857, 7, 2, 0, 0, 857, 858, 7, 3, 0, 0, 858, 119, 1, 0, 0, 0, 859, 860, 7, 15, 0, 0, 860, 861, 7, 1, 0, 0, 861, 862, 7, 6, 0, 0, 862, 863, 7, 2, 0, 0, 863, 864, 7, 5, 0, 0, 864, 121, 1, 0, 0, 0, 865, 866, 7, 1, 0, 0, 866, 867, 7, 9, 0, 0, 867, 123, 1, 0, 0, 0, 868, 869, 7, 1, 0, 0, 869, 870, 7, 2, 0, 0, 870, 125, 1, 0, 0, 0, 871, 872, 7, 13, 0, 0, 872, 873, 7, 12, 0, 0, 873, 874, 7, 2, 0, 0, 874, 875, 7, 5, 0, 0, 875, 127, 1, 0, 0, 0, 876, 877, 7, 13, 0, 0, 877, 878, 7, 1, 0, 0, 878, 879, 7, 18, 0, 0, 879, 880, 7, 3, 0, 0, 880, 129, 1, 0, 0, 0, 881, 882, 5, 40, 0, 0, 882, 131, 1, 0, 0, 0, 883, 884, 7, 9, 0, 0, 884, 885, 7, 7, 0, 0, 885, 886, 7, 5, 0, 0, 886, 133, 1, 0, 0, 0, 887, 888, 7, 9, 0, 0, 888, 889, 7, 20, 0, 0, 889, 890, 7, 13, 0, 0, 890, 891, 7, 13, 0, 0, 891, 135, 1, 0, 0, 0, 892, 893, 7, 9, 0, 0, 893, 894, 7, 20, 0, 0, 894, 895, 7, 13, 0, 0, 895, 896, 7, 13, 0, 0, 896, 897, 7, 2, 0, 0, 897, 137, 1, 0, 0, 0, 898, 899, 7, 7, 0, 0, 899, 900, 7, 6, 0, 0, 900, 139, 1, 0, 0, 0, 901, 902, 5, 63, 0, 0, 902, 141, 1, 0, 0, 0, 903, 904, 7, 6, 0, 0, 904, 905, 7, 13, 0, 0, 905, 906, 7, 1, 0, 0, 906, 907, 7, 18, 0, 0, 907, 908, 7, 3, 0, 0, 908, 143, 1, 0, 0, 0, 909, 910, 5, 41, 0, 0, 910, 145, 1, 0, 0, 0, 911, 912, 7, 5, 0, 0, 912, 913, 7, 6, 0, 0, 913, 914, 7, 20, 0, 0, 914, 915, 7, 3, 0, 0, 915, 147, 1, 0, 0, 0, 916, 917, 5, 61, 0, 0, 917, 918, 5, 61, 0, 0, 918, 149, 1, 0, 0, 0, 919, 920, 5, 61, 0, 0, 920, 921, 5, 126, 0, 0, 921, 151, 1, 0, 0, 0, 922, 923, 5, 33, 0, 0, 923, 924, 5, 61, 0, 0, 924, 153, 1, 0, 0, 0, 925, 926, 5, 60, 0, 0, 926, 155, 1, 0, 0, 0, 927, 928, 5, 60, 0, 0, 928, 929, 5, 61, 0, 0, 929, 157, 1, 0, 0, 0, 930, 931, 5, 62, 0, 0, 931, 159, 1, 0, 0, 0, 932, 933, 5, 62, 0, 0, 933, 934, 5, 61, 0, 0, 934, 161, 1, 0, 0, 0, 935, 936, 5, 43, 0, 0, 936, 163, 1, 0, 0, 0, 937, 938, 5, 45, 0, 0, 938, 165, 1, 0, 0, 0, 939, 940, 5, 42, 0, 0, 940, 167, 1, 0, 0, 0, 941, 942, 5, 47, 0, 0, 942, 169, 1, 0, 0, 0, 943, 944, 5, 37, 0, 0, 944, 171, 1, 0, 0, 0, 945, 946, 4, 78, 8, 0, 946, 947, 5, 123, 0, 0, 947, 173, 1, 0, 0, 0, 948, 949, 4, 79, 9, 0, 949, 950, 5, 125, 0, 0, 950, 175, 1, 0, 0, 0, 951, 952, 3, 46, 15, 0, 952, 953, 1, 0, 0, 0, 953, 954, 6, 80, 13, 0, 954, 177, 1, 0, 0, 0, 955, 958, 3, 140, 62, 0, 956, 959, 3, 76, 30, 0, 957, 959, 3, 90, 37, 0, 958, 956, 1, 0, 0, 0, 958, 957, 1, 0, 0, 0, 959, 963, 1, 0, 0, 0, 960, 962, 3, 92, 38, 0, 961, 960, 1, 0, 0, 0, 962, 965, 1, 0, 0, 0, 963, 961, 1, 0, 0, 0, 963, 964, 1, 0, 0, 0, 964, 973, 1, 0, 0, 0, 965, 963, 1, 0, 0, 0, 966, 968, 3, 140, 62, 0, 967, 969, 3, 74, 29, 0, 968, 967, 1, 0, 0, 0, 969, 970, 1, 0, 0, 0, 970, 968, 1, 0, 0, 0, 970, 971, 1, 0, 0, 0, 971, 973, 1, 0, 0, 0, 972, 955, 1, 0, 0, 0, 972, 966, 1, 0, 0, 0, 973, 179, 1, 0, 0, 0, 974, 975, 5, 91, 0, 0, 975, 976, 1, 0, 0, 0, 976, 977, 6, 82, 0, 0, 977, 978, 6, 82, 0, 0, 978, 181, 1, 0, 0, 0, 979, 980, 5, 93, 0, 0, 980, 981, 1, 0, 0, 0, 981, 982, 6, 83, 12, 0, 982, 983, 6, 83, 12, 0, 983, 183, 1, 0, 0, 0, 984, 988, 3, 76, 30, 0, 985, 987, 3, 92, 38, 0, 986, 985, 1, 0, 0, 0, 987, 990, 1, 0, 0, 0, 988, 986, 1, 0, 0, 0, 988, 989, 1, 0, 0, 0, 989, 1001, 1, 0, 0, 0, 990, 988, 1, 0, 0, 0, 991, 994, 3, 90, 37, 0, 992, 994, 3, 84, 34, 0, 993, 991, 1, 0, 0, 0, 993, 992, 1, 0, 0, 0, 994, 996, 1, 0, 0, 0, 995, 997, 3, 92, 38, 0, 996, 995, 1, 0, 0, 0, 997, 998, 1, 0, 0, 0, 998, 996, 1, 0, 0, 0, 998, 999, 1, 0, 0, 0, 999, 1001, 1, 0, 0, 0, 1000, 984, 1, 0, 0, 0, 1000, 993, 1, 0, 0, 0, 1001, 185, 1, 0, 0, 0, 1002, 1004, 3, 86, 35, 0, 1003, 1005, 3, 88, 36, 0, 1004, 1003, 1, 0, 0, 0, 1005, 1006, 1, 0, 0, 0, 1006, 1004, 1, 0, 0, 0, 1006, 1007, 1, 0, 0, 0, 1007, 1008, 1, 0, 0, 0, 1008, 1009, 3, 86, 35, 0, 1009, 187, 1, 0, 0, 0, 1010, 1011, 3, 186, 85, 0, 1011, 189, 1, 0, 0, 0, 1012, 1013, 3, 66, 25, 0, 1013, 1014, 1, 0, 0, 0, 1014, 1015, 6, 87, 11, 0, 1015, 191, 1, 0, 0, 0, 1016, 1017, 3, 68, 26, 0, 1017, 1018, 1, 0, 0, 0, 1018, 1019, 6, 88, 11, 0, 1019, 193, 1, 0, 0, 0, 1020, 1021, 3, 70, 27, 0, 1021, 1022, 1, 0, 0, 0, 1022, 1023, 6, 89, 11, 0, 1023, 195, 1, 0, 0, 0, 1024, 1025, 3, 180, 82, 0, 1025, 1026, 1, 0, 0, 0, 1026, 1027, 6, 90, 14, 0, 1027, 1028, 6, 90, 15, 0, 1028, 197, 1, 0, 0, 0, 1029, 1030, 3, 72, 28, 0, 1030, 1031, 1, 0, 0, 0, 1031, 1032, 6, 91, 16, 0, 1032, 1033, 6, 91, 12, 0, 1033, 199, 1, 0, 0, 0, 1034, 1035, 3, 70, 27, 0, 1035, 1036, 1, 0, 0, 0, 1036, 1037, 6, 92, 11, 0, 1037, 201, 1, 0, 0, 0, 1038, 1039, 3, 66, 25, 0, 1039, 1040, 1, 0, 0, 0, 1040, 1041, 6, 93, 11, 0, 1041, 203, 1, 0, 0, 0, 1042, 1043, 3, 68, 26, 0, 1043, 1044, 1, 0, 0, 0, 1044, 1045, 6, 94, 11, 0, 1045, 205, 1, 0, 0, 0, 1046, 1047, 3, 72, 28, 0, 1047, 1048, 1, 0, 0, 0, 1048, 1049, 6, 95, 16, 0, 1049, 1050, 6, 95, 12, 0, 1050, 207, 1, 0, 0, 0, 1051, 1052, 3, 180, 82, 0, 1052, 1053, 1, 0, 0, 0, 1053, 1054, 6, 96, 14, 0, 1054, 209, 1, 0, 0, 0, 1055, 1056, 3, 182, 83, 0, 1056, 1057, 1, 0, 0, 0, 1057, 1058, 6, 97, 17, 0, 1058, 211, 1, 0, 0, 0, 1059, 1060, 3, 110, 47, 0, 1060, 1061, 1, 0, 0, 0, 1061, 1062, 6, 98, 18, 0, 1062, 213, 1, 0, 0, 0, 1063, 1064, 3, 112, 48, 0, 1064, 1065, 1, 0, 0, 0, 1065, 1066, 6, 99, 19, 0, 1066, 215, 1, 0, 0, 0, 1067, 1068, 3, 106, 45, 0, 1068, 1069, 1, 0, 0, 0, 1069, 1070, 6, 100, 20, 0, 1070, 217, 1, 0, 0, 0, 1071, 1072, 7, 16, 0, 0, 1072, 1073, 7, 3, 0, 0, 1073, 1074, 7, 5, 0, 0, 1074, 1075, 7, 12, 0, 0, 1075, 1076, 7, 0, 0, 0, 1076, 1077, 7, 12, 0, 0, 1077, 1078, 7, 5, 0, 0, 1078, 1079, 7, 12, 0, 0, 1079, 219, 1, 0, 0, 0, 1080, 1084, 8, 33, 0, 0, 1081, 1082, 5, 47, 0, 0, 1082, 1084, 8, 34, 0, 0, 1083, 1080, 1, 0, 0, 0, 1083, 1081, 1, 0, 0, 0, 1084, 221, 1, 0, 0, 0, 1085, 1087, 3, 220, 102, 0, 1086, 1085, 1, 0, 0, 0, 1087, 1088, 1, 0, 0, 0, 1088, 1086, 1, 0, 0, 0, 1088, 1089, 1, 0, 0, 0, 1089, 223, 1, 0, 0, 0, 1090, 1091, 3, 222, 103, 0, 1091, 1092, 1, 0, 0, 0, 1092, 1093, 6, 104, 21, 0, 1093, 225, 1, 0, 0, 0, 1094, 1095, 3, 94, 39, 0, 1095, 1096, 1, 0, 0, 0, 1096, 1097, 6, 105, 22, 0, 1097, 227, 1, 0, 0, 0, 1098, 1099, 3, 66, 25, 0, 1099, 1100, 1, 0, 0, 0, 1100, 1101, 6, 106, 11, 0, 1101, 229, 1, 0, 0, 0, 1102, 1103, 3, 68, 26, 0, 1103, 1104, 1, 0, 0, 0, 1104, 1105, 6, 107, 11, 0, 1105, 231, 1, 0, 0, 0, 1106, 1107, 3, 70, 27, 0, 1107, 1108, 1, 0, 0, 0, 1108, 1109, 6, 108, 11, 0, 1109, 233, 1, 0, 0, 0, 1110, 1111, 3, 72, 28, 0, 1111, 1112, 1, 0, 0, 0, 1112, 1113, 6, 109, 16, 0, 1113, 1114, 6, 109, 12, 0, 1114, 235, 1, 0, 0, 0, 1115, 1116, 3, 116, 50, 0, 1116, 1117, 1, 0, 0, 0, 1117, 1118, 6, 110, 23, 0, 1118, 237, 1, 0, 0, 0, 1119, 1120, 3, 112, 48, 0, 1120, 1121, 1, 0, 0, 0, 1121, 1122, 6, 111, 19, 0, 1122, 239, 1, 0, 0, 0, 1123, 1124, 4, 112, 10, 0, 1124, 1125, 3, 140, 62, 0, 1125, 1126, 1, 0, 0, 0, 1126, 1127, 6, 112, 24, 0, 1127, 241, 1, 0, 0, 0, 1128, 1129, 4, 113, 11, 0, 1129, 1130, 3, 178, 81, 0, 1130, 1131, 1, 0, 0, 0, 1131, 1132, 6, 113, 25, 0, 1132, 243, 1, 0, 0, 0, 1133, 1138, 3, 76, 30, 0, 1134, 1138, 3, 74, 29, 0, 1135, 1138, 3, 90, 37, 0, 1136, 1138, 3, 166, 75, 0, 1137, 1133, 1, 0, 0, 0, 1137, 1134, 1, 0, 0, 0, 1137, 1135, 1, 0, 0, 0, 1137, 1136, 1, 0, 0, 0, 1138, 245, 1, 0, 0, 0, 1139, 1142, 3, 76, 30, 0, 1140, 1142, 3, 166, 75, 0, 1141, 1139, 1, 0, 0, 0, 1141, 1140, 1, 0, 0, 0, 1142, 1146, 1, 0, 0, 0, 1143, 1145, 3, 244, 114, 0, 1144, 1143, 1, 0, 0, 0, 1145, 1148, 1, 0, 0, 0, 1146, 1144, 1, 0, 0, 0, 1146, 1147, 1, 0, 0, 0, 1147, 1159, 1, 0, 0, 0, 1148, 1146, 1, 0, 0, 0, 1149, 1152, 3, 90, 37, 0, 1150, 1152, 3, 84, 34, 0, 1151, 1149, 1, 0, 0, 0, 1151, 1150, 1, 0, 0, 0, 1152, 1154, 1, 0, 0, 0, 1153, 1155, 3, 244, 114, 0, 1154, 1153, 1, 0, 0, 0, 1155, 1156, 1, 0, 0, 0, 1156, 1154, 1, 0, 0, 0, 1156, 1157, 1, 0, 0, 0, 1157, 1159, 1, 0, 0, 0, 1158, 1141, 1, 0, 0, 0, 1158, 1151, 1, 0, 0, 0, 1159, 247, 1, 0, 0, 0, 1160, 1163, 3, 246, 115, 0, 1161, 1163, 3, 186, 85, 0, 1162, 1160, 1, 0, 0, 0, 1162, 1161, 1, 0, 0, 0, 1163, 1164, 1, 0, 0, 0, 1164, 1162, 1, 0, 0, 0, 1164, 1165, 1, 0, 0, 0, 1165, 249, 1, 0, 0, 0, 1166, 1167, 3, 66, 25, 0, 1167, 1168, 1, 0, 0, 0, 1168, 1169, 6, 117, 11, 0, 1169, 251, 1, 0, 0, 0, 1170, 1171, 3, 68, 26, 0, 1171, 1172, 1, 0, 0, 0, 1172, 1173, 6, 118, 11, 0, 1173, 253, 1, 0, 0, 0, 1174, 1175, 3, 70, 27, 0, 1175, 1176, 1, 0, 0, 0, 1176, 1177, 6, 119, 11, 0, 1177, 255, 1, 0, 0, 0, 1178, 1179, 3, 72, 28, 0, 1179, 1180, 1, 0, 0, 0, 1180, 1181, 6, 120, 16, 0, 1181, 1182, 6, 120, 12, 0, 1182, 257, 1, 0, 0, 0, 1183, 1184, 3, 106, 45, 0, 1184, 1185, 1, 0, 0, 0, 1185, 1186, 6, 121, 20, 0, 1186, 259, 1, 0, 0, 0, 1187, 1188, 3, 112, 48, 0, 1188, 1189, 1, 0, 0, 0, 1189, 1190, 6, 122, 19, 0, 1190, 261, 1, 0, 0, 0, 1191, 1192, 3, 116, 50, 0, 1192, 1193, 1, 0, 0, 0, 1193, 1194, 6, 123, 23, 0, 1194, 263, 1, 0, 0, 0, 1195, 1196, 4, 124, 12, 0, 1196, 1197, 3, 140, 62, 0, 1197, 1198, 1, 0, 0, 0, 1198, 1199, 6, 124, 24, 0, 1199, 265, 1, 0, 0, 0, 1200, 1201, 4, 125, 13, 0, 1201, 1202, 3, 178, 81, 0, 1202, 1203, 1, 0, 0, 0, 1203, 1204, 6, 125, 25, 0, 1204, 267, 1, 0, 0, 0, 1205, 1206, 7, 12, 0, 0, 1206, 1207, 7, 2, 0, 0, 1207, 269, 1, 0, 0, 0, 1208, 1209, 3, 248, 116, 0, 1209, 1210, 1, 0, 0, 0, 1210, 1211, 6, 127, 26, 0, 1211, 271, 1, 0, 0, 0, 1212, 1213, 3, 66, 25, 0, 1213, 1214, 1, 0, 0, 0, 1214, 1215, 6, 128, 11, 0, 1215, 273, 1, 0, 0, 0, 1216, 1217, 3, 68, 26, 0, 1217, 1218, 1, 0, 0, 0, 1218, 1219, 6, 129, 11, 0, 1219, 275, 1, 0, 0, 0, 1220, 1221, 3, 70, 27, 0, 1221, 1222, 1, 0, 0, 0, 1222, 1223, 6, 130, 11, 0, 1223, 277, 1, 0, 0, 0, 1224, 1225, 3, 72, 28, 0, 1225, 1226, 1, 0, 0, 0, 1226, 1227, 6, 131, 16, 0, 1227, 1228, 6, 131, 12, 0, 1228, 279, 1, 0, 0, 0, 1229, 1230, 3, 180, 82, 0, 1230, 1231, 1, 0, 0, 0, 1231, 1232, 6, 132, 14, 0, 1232, 1233, 6, 132, 27, 0, 1233, 281, 1, 0, 0, 0, 1234, 1235, 7, 7, 0, 0, 1235, 1236, 7, 9, 0, 0, 1236, 1237, 1, 0, 0, 0, 1237, 1238, 6, 133, 28, 0, 1238, 283, 1, 0, 0, 0, 1239, 1240, 7, 19, 0, 0, 1240, 1241, 7, 1, 0, 0, 1241, 1242, 7, 5, 0, 0, 1242, 1243, 7, 10, 0, 0, 1243, 1244, 1, 0, 0, 0, 1244, 1245, 6, 134, 28, 0, 1245, 285, 1, 0, 0, 0, 1246, 1247, 8, 35, 0, 0, 1247, 287, 1, 0, 0, 0, 1248, 1250, 3, 286, 135, 0, 1249, 1248, 1, 0, 0, 0, 1250, 1251, 1, 0, 0, 0, 1251, 1249, 1, 0, 0, 0, 1251, 1252, 1, 0, 0, 0, 1252, 1253, 1, 0, 0, 0, 1253, 1254, 3, 110, 47, 0, 1254, 1256, 1, 0, 0, 0, 1255, 1249, 1, 0, 0, 0, 1255, 1256, 1, 0, 0, 0, 1256, 1258, 1, 0, 0, 0, 1257, 1259, 3, 286, 135, 0, 1258, 1257, 1, 0, 0, 0, 1259, 1260, 1, 0, 0, 0, 1260, 1258, 1, 0, 0, 0, 1260, 1261, 1, 0, 0, 0, 1261, 289, 1, 0, 0, 0, 1262, 1263, 3, 288, 136, 0, 1263, 1264, 1, 0, 0, 0, 1264, 1265, 6, 137, 29, 0, 1265, 291, 1, 0, 0, 0, 1266, 1267, 3, 66, 25, 0, 1267, 1268, 1, 0, 0, 0, 1268, 1269, 6, 138, 11, 0, 1269, 293, 1, 0, 0, 0, 1270, 1271, 3, 68, 26, 0, 1271, 1272, 1, 0, 0, 0, 1272, 1273, 6, 139, 11, 0, 1273, 295, 1, 0, 0, 0, 1274, 1275, 3, 70, 27, 0, 1275, 1276, 1, 0, 0, 0, 1276, 1277, 6, 140, 11, 0, 1277, 297, 1, 0, 0, 0, 1278, 1279, 3, 72, 28, 0, 1279, 1280, 1, 0, 0, 0, 1280, 1281, 6, 141, 16, 0, 1281, 1282, 6, 141, 12, 0, 1282, 1283, 6, 141, 12, 0, 1283, 299, 1, 0, 0, 0, 1284, 1285, 3, 106, 45, 0, 1285, 1286, 1, 0, 0, 0, 1286, 1287, 6, 142, 20, 0, 1287, 301, 1, 0, 0, 0, 1288, 1289, 3, 112, 48, 0, 1289, 1290, 1, 0, 0, 0, 1290, 1291, 6, 143, 19, 0, 1291, 303, 1, 0, 0, 0, 1292, 1293, 3, 116, 50, 0, 1293, 1294, 1, 0, 0, 0, 1294, 1295, 6, 144, 23, 0, 1295, 305, 1, 0, 0, 0, 1296, 1297, 3, 284, 134, 0, 1297, 1298, 1, 0, 0, 0, 1298, 1299, 6, 145, 30, 0, 1299, 307, 1, 0, 0, 0, 1300, 1301, 3, 248, 116, 0, 1301, 1302, 1, 0, 0, 0, 1302, 1303, 6, 146, 26, 0, 1303, 309, 1, 0, 0, 0, 1304, 1305, 3, 188, 86, 0, 1305, 1306, 1, 0, 0, 0, 1306, 1307, 6, 147, 31, 0, 1307, 311, 1, 0, 0, 0, 1308, 1309, 4, 148, 14, 0, 1309, 1310, 3, 140, 62, 0, 1310, 1311, 1, 0, 0, 0, 1311, 1312, 6, 148, 24, 0, 1312, 313, 1, 0, 0, 0, 1313, 1314, 4, 149, 15, 0, 1314, 1315, 3, 178, 81, 0, 1315, 1316, 1, 0, 0, 0, 1316, 1317, 6, 149, 25, 0, 1317, 315, 1, 0, 0, 0, 1318, 1319, 3, 66, 25, 0, 1319, 1320, 1, 0, 0, 0, 1320, 1321, 6, 150, 11, 0, 1321, 317, 1, 0, 0, 0, 1322, 1323, 3, 68, 26, 0, 1323, 1324, 1, 0, 0, 0, 1324, 1325, 6, 151, 11, 0, 1325, 319, 1, 0, 0, 0, 1326, 1327, 3, 70, 27, 0, 1327, 1328, 1, 0, 0, 0, 1328, 1329, 6, 152, 11, 0, 1329, 321, 1, 0, 0, 0, 1330, 1331, 3, 72, 28, 0, 1331, 1332, 1, 0, 0, 0, 1332, 1333, 6, 153, 16, 0, 1333, 1334, 6, 153, 12, 0, 1334, 323, 1, 0, 0, 0, 1335, 1336, 3, 116, 50, 0, 1336, 1337, 1, 0, 0, 0, 1337, 1338, 6, 154, 23, 0, 1338, 325, 1, 0, 0, 0, 1339, 1340, 4, 155, 16, 0, 1340, 1341, 3, 140, 62, 0, 1341, 1342, 1, 0, 0, 0, 1342, 1343, 6, 155, 24, 0, 1343, 327, 1, 0, 0, 0, 1344, 1345, 4, 156, 17, 0, 1345, 1346, 3, 178, 81, 0, 1346, 1347, 1, 0, 0, 0, 1347, 1348, 6, 156, 25, 0, 1348, 329, 1, 0, 0, 0, 1349, 1350, 3, 188, 86, 0, 1350, 1351, 1, 0, 0, 0, 1351, 1352, 6, 157, 31, 0, 1352, 331, 1, 0, 0, 0, 1353, 1354, 3, 184, 84, 0, 1354, 1355, 1, 0, 0, 0, 1355, 1356, 6, 158, 32, 0, 1356, 333, 1, 0, 0, 0, 1357, 1358, 3, 66, 25, 0, 1358, 1359, 1, 0, 0, 0, 1359, 1360, 6, 159, 11, 0, 1360, 335, 1, 0, 0, 0, 1361, 1362, 3, 68, 26, 0, 1362, 1363, 1, 0, 0, 0, 1363, 1364, 6, 160, 11, 0, 1364, 337, 1, 0, 0, 0, 1365, 1366, 3, 70, 27, 0, 1366, 1367, 1, 0, 0, 0, 1367, 1368, 6, 161, 11, 0, 1368, 339, 1, 0, 0, 0, 1369, 1370, 3, 72, 28, 0, 1370, 1371, 1, 0, 0, 0, 1371, 1372, 6, 162, 16, 0, 1372, 1373, 6, 162, 12, 0, 1373, 341, 1, 0, 0, 0, 1374, 1375, 7, 1, 0, 0, 1375, 1376, 7, 9, 0, 0, 1376, 1377, 7, 15, 0, 0, 1377, 1378, 7, 7, 0, 0, 1378, 343, 1, 0, 0, 0, 1379, 1380, 3, 66, 25, 0, 1380, 1381, 1, 0, 0, 0, 1381, 1382, 6, 164, 11, 0, 1382, 345, 1, 0, 0, 0, 1383, 1384, 3, 68, 26, 0, 1384, 1385, 1, 0, 0, 0, 1385, 1386, 6, 165, 11, 0, 1386, 347, 1, 0, 0, 0, 1387, 1388, 3, 70, 27, 0, 1388, 1389, 1, 0, 0, 0, 1389, 1390, 6, 166, 11, 0, 1390, 349, 1, 0, 0, 0, 1391, 1392, 3, 182, 83, 0, 1392, 1393, 1, 0, 0, 0, 1393, 1394, 6, 167, 17, 0, 1394, 1395, 6, 167, 12, 0, 1395, 351, 1, 0, 0, 0, 1396, 1397, 3, 110, 47, 0, 1397, 1398, 1, 0, 0, 0, 1398, 1399, 6, 168, 18, 0, 1399, 353, 1, 0, 0, 0, 1400, 1406, 3, 84, 34, 0, 1401, 1406, 3, 74, 29, 0, 1402, 1406, 3, 116, 50, 0, 1403, 1406, 3, 76, 30, 0, 1404, 1406, 3, 90, 37, 0, 1405, 1400, 1, 0, 0, 0, 1405, 1401, 1, 0, 0, 0, 1405, 1402, 1, 0, 0, 0, 1405, 1403, 1, 0, 0, 0, 1405, 1404, 1, 0, 0, 0, 1406, 1407, 1, 0, 0, 0, 1407, 1405, 1, 0, 0, 0, 1407, 1408, 1, 0, 0, 0, 1408, 355, 1, 0, 0, 0, 1409, 1410, 3, 66, 25, 0, 1410, 1411, 1, 0, 0, 0, 1411, 1412, 6, 170, 11, 0, 1412, 357, 1, 0, 0, 0, 1413, 1414, 3, 68, 26, 0, 1414, 1415, 1, 0, 0, 0, 1415, 1416, 6, 171, 11, 0, 1416, 359, 1, 0, 0, 0, 1417, 1418, 3, 70, 27, 0, 1418, 1419, 1, 0, 0, 0, 1419, 1420, 6, 172, 11, 0, 1420, 361, 1, 0, 0, 0, 1421, 1422, 3, 72, 28, 0, 1422, 1423, 1, 0, 0, 0, 1423, 1424, 6, 173, 16, 0, 1424, 1425, 6, 173, 12, 0, 1425, 363, 1, 0, 0, 0, 1426, 1427, 3, 110, 47, 0, 1427, 1428, 1, 0, 0, 0, 1428, 1429, 6, 174, 18, 0, 1429, 365, 1, 0, 0, 0, 1430, 1431, 3, 112, 48, 0, 1431, 1432, 1, 0, 0, 0, 1432, 1433, 6, 175, 19, 0, 1433, 367, 1, 0, 0, 0, 1434, 1435, 3, 116, 50, 0, 1435, 1436, 1, 0, 0, 0, 1436, 1437, 6, 176, 23, 0, 1437, 369, 1, 0, 0, 0, 1438, 1439, 3, 282, 133, 0, 1439, 1440, 1, 0, 0, 0, 1440, 1441, 6, 177, 33, 0, 1441, 1442, 6, 177, 34, 0, 1442, 371, 1, 0, 0, 0, 1443, 1444, 3, 222, 103, 0, 1444, 1445, 1, 0, 0, 0, 1445, 1446, 6, 178, 21, 0, 1446, 373, 1, 0, 0, 0, 1447, 1448, 3, 94, 39, 0, 1448, 1449, 1, 0, 0, 0, 1449, 1450, 6, 179, 22, 0, 1450, 375, 1, 0, 0, 0, 1451, 1452, 3, 66, 25, 0, 1452, 1453, 1, 0, 0, 0, 1453, 1454, 6, 180, 11, 0, 1454, 377, 1, 0, 0, 0, 1455, 1456, 3, 68, 26, 0, 1456, 1457, 1, 0, 0, 0, 1457, 1458, 6, 181, 11, 0, 1458, 379, 1, 0, 0, 0, 1459, 1460, 3, 70, 27, 0, 1460, 1461, 1, 0, 0, 0, 1461, 1462, 6, 182, 11, 0, 1462, 381, 1, 0, 0, 0, 1463, 1464, 3, 72, 28, 0, 1464, 1465, 1, 0, 0, 0, 1465, 1466, 6, 183, 16, 0, 1466, 1467, 6, 183, 12, 0, 1467, 1468, 6, 183, 12, 0, 1468, 383, 1, 0, 0, 0, 1469, 1470, 3, 112, 48, 0, 1470, 1471, 1, 0, 0, 0, 1471, 1472, 6, 184, 19, 0, 1472, 385, 1, 0, 0, 0, 1473, 1474, 3, 116, 50, 0, 1474, 1475, 1, 0, 0, 0, 1475, 1476, 6, 185, 23, 0, 1476, 387, 1, 0, 0, 0, 1477, 1478, 3, 248, 116, 0, 1478, 1479, 1, 0, 0, 0, 1479, 1480, 6, 186, 26, 0, 1480, 389, 1, 0, 0, 0, 1481, 1482, 3, 66, 25, 0, 1482, 1483, 1, 0, 0, 0, 1483, 1484, 6, 187, 11, 0, 1484, 391, 1, 0, 0, 0, 1485, 1486, 3, 68, 26, 0, 1486, 1487, 1, 0, 0, 0, 1487, 1488, 6, 188, 11, 0, 1488, 393, 1, 0, 0, 0, 1489, 1490, 3, 70, 27, 0, 1490, 1491, 1, 0, 0, 0, 1491, 1492, 6, 189, 11, 0, 1492, 395, 1, 0, 0, 0, 1493, 1494, 3, 72, 28, 0, 1494, 1495, 1, 0, 0, 0, 1495, 1496, 6, 190, 16, 0, 1496, 1497, 6, 190, 12, 0, 1497, 397, 1, 0, 0, 0, 1498, 1499, 3, 54, 19, 0, 1499, 1500, 1, 0, 0, 0, 1500, 1501, 6, 191, 35, 0, 1501, 399, 1, 0, 0, 0, 1502, 1503, 3, 268, 126, 0, 1503, 1504, 1, 0, 0, 0, 1504, 1505, 6, 192, 36, 0, 1505, 401, 1, 0, 0, 0, 1506, 1507, 3, 282, 133, 0, 1507, 1508, 1, 0, 0, 0, 1508, 1509, 6, 193, 33, 0, 1509, 1510, 6, 193, 12, 0, 1510, 1511, 6, 193, 0, 0, 1511, 403, 1, 0, 0, 0, 1512, 1513, 7, 20, 0, 0, 1513, 1514, 7, 2, 0, 0, 1514, 1515, 7, 1, 0, 0, 1515, 1516, 7, 9, 0, 0, 1516, 1517, 7, 17, 0, 0, 1517, 1518, 1, 0, 0, 0, 1518, 1519, 6, 194, 12, 0, 1519, 1520, 6, 194, 0, 0, 1520, 405, 1, 0, 0, 0, 1521, 1522, 3, 222, 103, 0, 1522, 1523, 1, 0, 0, 0, 1523, 1524, 6, 195, 21, 0, 1524, 407, 1, 0, 0, 0, 1525, 1526, 3, 94, 39, 0, 1526, 1527, 1, 0, 0, 0, 1527, 1528, 6, 196, 22, 0, 1528, 409, 1, 0, 0, 0, 1529, 1530, 3, 110, 47, 0, 1530, 1531, 1, 0, 0, 0, 1531, 1532, 6, 197, 18, 0, 1532, 411, 1, 0, 0, 0, 1533, 1534, 3, 184, 84, 0, 1534, 1535, 1, 0, 0, 0, 1535, 1536, 6, 198, 32, 0, 1536, 413, 1, 0, 0, 0, 1537, 1538, 3, 188, 86, 0, 1538, 1539, 1, 0, 0, 0, 1539, 1540, 6, 199, 31, 0, 1540, 415, 1, 0, 0, 0, 1541, 1542, 3, 66, 25, 0, 1542, 1543, 1, 0, 0, 0, 1543, 1544, 6, 200, 11, 0, 1544, 417, 1, 0, 0, 0, 1545, 1546, 3, 68, 26, 0, 1546, 1547, 1, 0, 0, 0, 1547, 1548, 6, 201, 11, 0, 1548, 419, 1, 0, 0, 0, 1549, 1550, 3, 70, 27, 0, 1550, 1551, 1, 0, 0, 0, 1551, 1552, 6, 202, 11, 0, 1552, 421, 1, 0, 0, 0, 1553, 1554, 3, 72, 28, 0, 1554, 1555, 1, 0, 0, 0, 1555, 1556, 6, 203, 16, 0, 1556, 1557, 6, 203, 12, 0, 1557, 423, 1, 0, 0, 0, 1558, 1559, 3, 222, 103, 0, 1559, 1560, 1, 0, 0, 0, 1560, 1561, 6, 204, 21, 0, 1561, 1562, 6, 204, 12, 0, 1562, 1563, 6, 204, 37, 0, 1563, 425, 1, 0, 0, 0, 1564, 1565, 3, 94, 39, 0, 1565, 1566, 1, 0, 0, 0, 1566, 1567, 6, 205, 22, 0, 1567, 1568, 6, 205, 12, 0, 1568, 1569, 6, 205, 37, 0, 1569, 427, 1, 0, 0, 0, 1570, 1571, 3, 66, 25, 0, 1571, 1572, 1, 0, 0, 0, 1572, 1573, 6, 206, 11, 0, 1573, 429, 1, 0, 0, 0, 1574, 1575, 3, 68, 26, 0, 1575, 1576, 1, 0, 0, 0, 1576, 1577, 6, 207, 11, 0, 1577, 431, 1, 0, 0, 0, 1578, 1579, 3, 70, 27, 0, 1579, 1580, 1, 0, 0, 0, 1580, 1581, 6, 208, 11, 0, 1581, 433, 1, 0, 0, 0, 1582, 1583, 3, 110, 47, 0, 1583, 1584, 1, 0, 0, 0, 1584, 1585, 6, 209, 18, 0, 1585, 1586, 6, 209, 12, 0, 1586, 1587, 6, 209, 9, 0, 1587, 435, 1, 0, 0, 0, 1588, 1589, 3, 112, 48, 0, 1589, 1590, 1, 0, 0, 0, 1590, 1591, 6, 210, 19, 0, 1591, 1592, 6, 210, 12, 0, 1592, 1593, 6, 210, 9, 0, 1593, 437, 1, 0, 0, 0, 1594, 1595, 3, 66, 25, 0, 1595, 1596, 1, 0, 0, 0, 1596, 1597, 6, 211, 11, 0, 1597, 439, 1, 0, 0, 0, 1598, 1599, 3, 68, 26, 0, 1599, 1600, 1, 0, 0, 0, 1600, 1601, 6, 212, 11, 0, 1601, 441, 1, 0, 0, 0, 1602, 1603, 3, 70, 27, 0, 1603, 1604, 1, 0, 0, 0, 1604, 1605, 6, 213, 11, 0, 1605, 443, 1, 0, 0, 0, 1606, 1607, 3, 188, 86, 0, 1607, 1608, 1, 0, 0, 0, 1608, 1609, 6, 214, 12, 0, 1609, 1610, 6, 214, 0, 0, 1610, 1611, 6, 214, 31, 0, 1611, 445, 1, 0, 0, 0, 1612, 1613, 3, 184, 84, 0, 1613, 1614, 1, 0, 0, 0, 1614, 1615, 6, 215, 12, 0, 1615, 1616, 6, 215, 0, 0, 1616, 1617, 6, 215, 32, 0, 1617, 447, 1, 0, 0, 0, 1618, 1619, 3, 100, 42, 0, 1619, 1620, 1, 0, 0, 0, 1620, 1621, 6, 216, 12, 0, 1621, 1622, 6, 216, 0, 0, 1622, 1623, 6, 216, 38, 0, 1623, 449, 1, 0, 0, 0, 1624, 1625, 3, 72, 28, 0, 1625, 1626, 1, 0, 0, 0, 1626, 1627, 6, 217, 16, 0, 1627, 1628, 6, 217, 12, 0, 1628, 451, 1, 0, 0, 0, 66, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 665, 675, 679, 682, 691, 693, 704, 723, 728, 737, 744, 749, 751, 762, 770, 773, 775, 780, 785, 791, 798, 803, 809, 812, 820, 824, 958, 963, 970, 972, 988, 993, 998, 1000, 1006, 1083, 1088, 1137, 1141, 1146, 1151, 1156, 1158, 1162, 1164, 1251, 1255, 1260, 1405, 1407, 39, 5, 1, 0, 5, 4, 0, 5, 6, 0, 5, 2, 0, 5, 3, 0, 5, 8, 0, 5, 5, 0, 5, 9, 0, 5, 11, 0, 5, 14, 0, 5, 13, 0, 0, 1, 0, 4, 0, 0, 7, 16, 0, 7, 72, 0, 5, 0, 0, 7, 29, 0, 7, 73, 0, 7, 38, 0, 7, 39, 0, 7, 36, 0, 7, 83, 0, 7, 30, 0, 7, 41, 0, 7, 53, 0, 7, 71, 0, 7, 87, 0, 5, 10, 0, 5, 7, 0, 7, 97, 0, 7, 96, 0, 7, 75, 0, 7, 74, 0, 7, 95, 0, 5, 12, 0, 7, 20, 0, 7, 91, 0, 5, 15, 0, 7, 33, 0] \ No newline at end of file diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseLexer.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseLexer.java index 737f0465e1ab6..eb8af91bef274 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseLexer.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseLexer.java @@ -109,13 +109,13 @@ private static String[] makeRuleNames() { "LOOKUP_MULTILINE_COMMENT", "LOOKUP_WS", "LOOKUP_FIELD_PIPE", "LOOKUP_FIELD_COMMA", "LOOKUP_FIELD_DOT", "LOOKUP_FIELD_ID_PATTERN", "LOOKUP_FIELD_LINE_COMMENT", "LOOKUP_FIELD_MULTILINE_COMMENT", "LOOKUP_FIELD_WS", "JOIN_PIPE", "JOIN_JOIN", - "JOIN_AS", "JOIN_ON", "USING", "JOIN_UNQUOTED_IDENTIFER", "JOIN_QUOTED_IDENTIFIER", - "JOIN_LINE_COMMENT", "JOIN_MULTILINE_COMMENT", "JOIN_WS", "METRICS_PIPE", - "METRICS_UNQUOTED_SOURCE", "METRICS_QUOTED_SOURCE", "METRICS_LINE_COMMENT", - "METRICS_MULTILINE_COMMENT", "METRICS_WS", "CLOSING_METRICS_COLON", "CLOSING_METRICS_COMMA", - "CLOSING_METRICS_LINE_COMMENT", "CLOSING_METRICS_MULTILINE_COMMENT", - "CLOSING_METRICS_WS", "CLOSING_METRICS_QUOTED_IDENTIFIER", "CLOSING_METRICS_UNQUOTED_IDENTIFIER", - "CLOSING_METRICS_BY", "CLOSING_METRICS_PIPE" + "JOIN_AS", "JOIN_ON", "USING", "JOIN_UNQUOTED_SOURCE", "JOIN_QUOTED_SOURCE", + "JOIN_COLON", "JOIN_UNQUOTED_IDENTIFER", "JOIN_QUOTED_IDENTIFIER", "JOIN_LINE_COMMENT", + "JOIN_MULTILINE_COMMENT", "JOIN_WS", "METRICS_PIPE", "METRICS_UNQUOTED_SOURCE", + "METRICS_QUOTED_SOURCE", "METRICS_LINE_COMMENT", "METRICS_MULTILINE_COMMENT", + "METRICS_WS", "CLOSING_METRICS_COLON", "CLOSING_METRICS_COMMA", "CLOSING_METRICS_LINE_COMMENT", + "CLOSING_METRICS_MULTILINE_COMMENT", "CLOSING_METRICS_WS", "CLOSING_METRICS_QUOTED_IDENTIFIER", + "CLOSING_METRICS_UNQUOTED_IDENTIFIER", "CLOSING_METRICS_BY", "CLOSING_METRICS_PIPE" }; } public static final String[] ruleNames = makeRuleNames(); @@ -397,7 +397,7 @@ private boolean MVEXPAND_NAMED_OR_POSITIONAL_PARAM_sempred(RuleContext _localctx } public static final String _serializedATN = - "\u0004\u0000\u0082\u064b\u0006\uffff\uffff\u0006\uffff\uffff\u0006\uffff"+ + "\u0004\u0000\u0082\u065d\u0006\uffff\uffff\u0006\uffff\uffff\u0006\uffff"+ "\uffff\u0006\uffff\uffff\u0006\uffff\uffff\u0006\uffff\uffff\u0006\uffff"+ "\uffff\u0006\uffff\uffff\u0006\uffff\uffff\u0006\uffff\uffff\u0006\uffff"+ "\uffff\u0006\uffff\uffff\u0006\uffff\uffff\u0006\uffff\uffff\u0006\uffff"+ @@ -459,7 +459,8 @@ private boolean MVEXPAND_NAMED_OR_POSITIONAL_PARAM_sempred(RuleContext _localctx "\u00cb\u0002\u00cc\u0007\u00cc\u0002\u00cd\u0007\u00cd\u0002\u00ce\u0007"+ "\u00ce\u0002\u00cf\u0007\u00cf\u0002\u00d0\u0007\u00d0\u0002\u00d1\u0007"+ "\u00d1\u0002\u00d2\u0007\u00d2\u0002\u00d3\u0007\u00d3\u0002\u00d4\u0007"+ - "\u00d4\u0002\u00d5\u0007\u00d5\u0002\u00d6\u0007\u00d6\u0001\u0000\u0001"+ + "\u00d4\u0002\u00d5\u0007\u00d5\u0002\u00d6\u0007\u00d6\u0002\u00d7\u0007"+ + "\u00d7\u0002\u00d8\u0007\u00d8\u0002\u00d9\u0007\u00d9\u0001\u0000\u0001"+ "\u0000\u0001\u0000\u0001\u0000\u0001\u0000\u0001\u0000\u0001\u0000\u0001"+ "\u0000\u0001\u0000\u0001\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001"+ "\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u0001\u0002\u0001"+ @@ -492,28 +493,28 @@ private boolean MVEXPAND_NAMED_OR_POSITIONAL_PARAM_sempred(RuleContext _localctx "\u0015\u0001\u0015\u0001\u0016\u0001\u0016\u0001\u0016\u0001\u0016\u0001"+ "\u0016\u0001\u0016\u0001\u0016\u0001\u0016\u0001\u0016\u0001\u0017\u0001"+ "\u0017\u0001\u0017\u0001\u0017\u0001\u0017\u0001\u0017\u0001\u0017\u0001"+ - "\u0017\u0001\u0017\u0001\u0017\u0001\u0018\u0004\u0018\u0292\b\u0018\u000b"+ - "\u0018\f\u0018\u0293\u0001\u0018\u0001\u0018\u0001\u0019\u0001\u0019\u0001"+ - "\u0019\u0001\u0019\u0005\u0019\u029c\b\u0019\n\u0019\f\u0019\u029f\t\u0019"+ - "\u0001\u0019\u0003\u0019\u02a2\b\u0019\u0001\u0019\u0003\u0019\u02a5\b"+ + "\u0017\u0001\u0017\u0001\u0017\u0001\u0018\u0004\u0018\u0298\b\u0018\u000b"+ + "\u0018\f\u0018\u0299\u0001\u0018\u0001\u0018\u0001\u0019\u0001\u0019\u0001"+ + "\u0019\u0001\u0019\u0005\u0019\u02a2\b\u0019\n\u0019\f\u0019\u02a5\t\u0019"+ + "\u0001\u0019\u0003\u0019\u02a8\b\u0019\u0001\u0019\u0003\u0019\u02ab\b"+ "\u0019\u0001\u0019\u0001\u0019\u0001\u001a\u0001\u001a\u0001\u001a\u0001"+ - "\u001a\u0001\u001a\u0005\u001a\u02ae\b\u001a\n\u001a\f\u001a\u02b1\t\u001a"+ + "\u001a\u0001\u001a\u0005\u001a\u02b4\b\u001a\n\u001a\f\u001a\u02b7\t\u001a"+ "\u0001\u001a\u0001\u001a\u0001\u001a\u0001\u001a\u0001\u001a\u0001\u001b"+ - "\u0004\u001b\u02b9\b\u001b\u000b\u001b\f\u001b\u02ba\u0001\u001b\u0001"+ + "\u0004\u001b\u02bf\b\u001b\u000b\u001b\f\u001b\u02c0\u0001\u001b\u0001"+ "\u001b\u0001\u001c\u0001\u001c\u0001\u001c\u0001\u001c\u0001\u001d\u0001"+ "\u001d\u0001\u001e\u0001\u001e\u0001\u001f\u0001\u001f\u0001\u001f\u0001"+ - " \u0001 \u0001!\u0001!\u0003!\u02ce\b!\u0001!\u0004!\u02d1\b!\u000b!\f"+ - "!\u02d2\u0001\"\u0001\"\u0001#\u0001#\u0001$\u0001$\u0001$\u0003$\u02dc"+ - "\b$\u0001%\u0001%\u0001&\u0001&\u0001&\u0003&\u02e3\b&\u0001\'\u0001\'"+ - "\u0001\'\u0005\'\u02e8\b\'\n\'\f\'\u02eb\t\'\u0001\'\u0001\'\u0001\'\u0001"+ - "\'\u0001\'\u0001\'\u0005\'\u02f3\b\'\n\'\f\'\u02f6\t\'\u0001\'\u0001\'"+ - "\u0001\'\u0001\'\u0001\'\u0003\'\u02fd\b\'\u0001\'\u0003\'\u0300\b\'\u0003"+ - "\'\u0302\b\'\u0001(\u0004(\u0305\b(\u000b(\f(\u0306\u0001)\u0004)\u030a"+ - "\b)\u000b)\f)\u030b\u0001)\u0001)\u0005)\u0310\b)\n)\f)\u0313\t)\u0001"+ - ")\u0001)\u0004)\u0317\b)\u000b)\f)\u0318\u0001)\u0004)\u031c\b)\u000b"+ - ")\f)\u031d\u0001)\u0001)\u0005)\u0322\b)\n)\f)\u0325\t)\u0003)\u0327\b"+ - ")\u0001)\u0001)\u0001)\u0001)\u0004)\u032d\b)\u000b)\f)\u032e\u0001)\u0001"+ - ")\u0003)\u0333\b)\u0001*\u0001*\u0001*\u0001+\u0001+\u0001+\u0001+\u0001"+ + " \u0001 \u0001!\u0001!\u0003!\u02d4\b!\u0001!\u0004!\u02d7\b!\u000b!\f"+ + "!\u02d8\u0001\"\u0001\"\u0001#\u0001#\u0001$\u0001$\u0001$\u0003$\u02e2"+ + "\b$\u0001%\u0001%\u0001&\u0001&\u0001&\u0003&\u02e9\b&\u0001\'\u0001\'"+ + "\u0001\'\u0005\'\u02ee\b\'\n\'\f\'\u02f1\t\'\u0001\'\u0001\'\u0001\'\u0001"+ + "\'\u0001\'\u0001\'\u0005\'\u02f9\b\'\n\'\f\'\u02fc\t\'\u0001\'\u0001\'"+ + "\u0001\'\u0001\'\u0001\'\u0003\'\u0303\b\'\u0001\'\u0003\'\u0306\b\'\u0003"+ + "\'\u0308\b\'\u0001(\u0004(\u030b\b(\u000b(\f(\u030c\u0001)\u0004)\u0310"+ + "\b)\u000b)\f)\u0311\u0001)\u0001)\u0005)\u0316\b)\n)\f)\u0319\t)\u0001"+ + ")\u0001)\u0004)\u031d\b)\u000b)\f)\u031e\u0001)\u0004)\u0322\b)\u000b"+ + ")\f)\u0323\u0001)\u0001)\u0005)\u0328\b)\n)\f)\u032b\t)\u0003)\u032d\b"+ + ")\u0001)\u0001)\u0001)\u0001)\u0004)\u0333\b)\u000b)\f)\u0334\u0001)\u0001"+ + ")\u0003)\u0339\b)\u0001*\u0001*\u0001*\u0001+\u0001+\u0001+\u0001+\u0001"+ ",\u0001,\u0001,\u0001,\u0001-\u0001-\u0001.\u0001.\u0001.\u0001/\u0001"+ "/\u00010\u00010\u00011\u00011\u00011\u00011\u00011\u00012\u00012\u0001"+ "3\u00013\u00013\u00013\u00013\u00013\u00014\u00014\u00014\u00014\u0001"+ @@ -526,12 +527,12 @@ private boolean MVEXPAND_NAMED_OR_POSITIONAL_PARAM_sempred(RuleContext _localctx "D\u0001D\u0001E\u0001E\u0001F\u0001F\u0001F\u0001G\u0001G\u0001H\u0001"+ "H\u0001H\u0001I\u0001I\u0001J\u0001J\u0001K\u0001K\u0001L\u0001L\u0001"+ "M\u0001M\u0001N\u0001N\u0001N\u0001O\u0001O\u0001O\u0001P\u0001P\u0001"+ - "P\u0001P\u0001Q\u0001Q\u0001Q\u0003Q\u03b9\bQ\u0001Q\u0005Q\u03bc\bQ\n"+ - "Q\fQ\u03bf\tQ\u0001Q\u0001Q\u0004Q\u03c3\bQ\u000bQ\fQ\u03c4\u0003Q\u03c7"+ + "P\u0001P\u0001Q\u0001Q\u0001Q\u0003Q\u03bf\bQ\u0001Q\u0005Q\u03c2\bQ\n"+ + "Q\fQ\u03c5\tQ\u0001Q\u0001Q\u0004Q\u03c9\bQ\u000bQ\fQ\u03ca\u0003Q\u03cd"+ "\bQ\u0001R\u0001R\u0001R\u0001R\u0001R\u0001S\u0001S\u0001S\u0001S\u0001"+ - "S\u0001T\u0001T\u0005T\u03d5\bT\nT\fT\u03d8\tT\u0001T\u0001T\u0003T\u03dc"+ - "\bT\u0001T\u0004T\u03df\bT\u000bT\fT\u03e0\u0003T\u03e3\bT\u0001U\u0001"+ - "U\u0004U\u03e7\bU\u000bU\fU\u03e8\u0001U\u0001U\u0001V\u0001V\u0001W\u0001"+ + "S\u0001T\u0001T\u0005T\u03db\bT\nT\fT\u03de\tT\u0001T\u0001T\u0003T\u03e2"+ + "\bT\u0001T\u0004T\u03e5\bT\u000bT\fT\u03e6\u0003T\u03e9\bT\u0001U\u0001"+ + "U\u0004U\u03ed\bU\u000bU\fU\u03ee\u0001U\u0001U\u0001V\u0001V\u0001W\u0001"+ "W\u0001W\u0001W\u0001X\u0001X\u0001X\u0001X\u0001Y\u0001Y\u0001Y\u0001"+ "Y\u0001Z\u0001Z\u0001Z\u0001Z\u0001Z\u0001[\u0001[\u0001[\u0001[\u0001"+ "[\u0001\\\u0001\\\u0001\\\u0001\\\u0001]\u0001]\u0001]\u0001]\u0001^\u0001"+ @@ -539,15 +540,15 @@ private boolean MVEXPAND_NAMED_OR_POSITIONAL_PARAM_sempred(RuleContext _localctx "`\u0001`\u0001a\u0001a\u0001a\u0001a\u0001b\u0001b\u0001b\u0001b\u0001"+ "c\u0001c\u0001c\u0001c\u0001d\u0001d\u0001d\u0001d\u0001e\u0001e\u0001"+ "e\u0001e\u0001e\u0001e\u0001e\u0001e\u0001e\u0001f\u0001f\u0001f\u0003"+ - "f\u0436\bf\u0001g\u0004g\u0439\bg\u000bg\fg\u043a\u0001h\u0001h\u0001"+ + "f\u043c\bf\u0001g\u0004g\u043f\bg\u000bg\fg\u0440\u0001h\u0001h\u0001"+ "h\u0001h\u0001i\u0001i\u0001i\u0001i\u0001j\u0001j\u0001j\u0001j\u0001"+ "k\u0001k\u0001k\u0001k\u0001l\u0001l\u0001l\u0001l\u0001m\u0001m\u0001"+ "m\u0001m\u0001m\u0001n\u0001n\u0001n\u0001n\u0001o\u0001o\u0001o\u0001"+ "o\u0001p\u0001p\u0001p\u0001p\u0001p\u0001q\u0001q\u0001q\u0001q\u0001"+ - "q\u0001r\u0001r\u0001r\u0001r\u0003r\u046c\br\u0001s\u0001s\u0003s\u0470"+ - "\bs\u0001s\u0005s\u0473\bs\ns\fs\u0476\ts\u0001s\u0001s\u0003s\u047a\b"+ - "s\u0001s\u0004s\u047d\bs\u000bs\fs\u047e\u0003s\u0481\bs\u0001t\u0001"+ - "t\u0004t\u0485\bt\u000bt\ft\u0486\u0001u\u0001u\u0001u\u0001u\u0001v\u0001"+ + "q\u0001r\u0001r\u0001r\u0001r\u0003r\u0472\br\u0001s\u0001s\u0003s\u0476"+ + "\bs\u0001s\u0005s\u0479\bs\ns\fs\u047c\ts\u0001s\u0001s\u0003s\u0480\b"+ + "s\u0001s\u0004s\u0483\bs\u000bs\fs\u0484\u0003s\u0487\bs\u0001t\u0001"+ + "t\u0004t\u048b\bt\u000bt\ft\u048c\u0001u\u0001u\u0001u\u0001u\u0001v\u0001"+ "v\u0001v\u0001v\u0001w\u0001w\u0001w\u0001w\u0001x\u0001x\u0001x\u0001"+ "x\u0001x\u0001y\u0001y\u0001y\u0001y\u0001z\u0001z\u0001z\u0001z\u0001"+ "{\u0001{\u0001{\u0001{\u0001|\u0001|\u0001|\u0001|\u0001|\u0001}\u0001"+ @@ -558,9 +559,9 @@ private boolean MVEXPAND_NAMED_OR_POSITIONAL_PARAM_sempred(RuleContext _localctx "\u0083\u0001\u0084\u0001\u0084\u0001\u0084\u0001\u0084\u0001\u0084\u0001"+ "\u0085\u0001\u0085\u0001\u0085\u0001\u0085\u0001\u0085\u0001\u0086\u0001"+ "\u0086\u0001\u0086\u0001\u0086\u0001\u0086\u0001\u0086\u0001\u0086\u0001"+ - "\u0087\u0001\u0087\u0001\u0088\u0004\u0088\u04dc\b\u0088\u000b\u0088\f"+ - "\u0088\u04dd\u0001\u0088\u0001\u0088\u0003\u0088\u04e2\b\u0088\u0001\u0088"+ - "\u0004\u0088\u04e5\b\u0088\u000b\u0088\f\u0088\u04e6\u0001\u0089\u0001"+ + "\u0087\u0001\u0087\u0001\u0088\u0004\u0088\u04e2\b\u0088\u000b\u0088\f"+ + "\u0088\u04e3\u0001\u0088\u0001\u0088\u0003\u0088\u04e8\b\u0088\u0001\u0088"+ + "\u0004\u0088\u04eb\b\u0088\u000b\u0088\f\u0088\u04ec\u0001\u0089\u0001"+ "\u0089\u0001\u0089\u0001\u0089\u0001\u008a\u0001\u008a\u0001\u008a\u0001"+ "\u008a\u0001\u008b\u0001\u008b\u0001\u008b\u0001\u008b\u0001\u008c\u0001"+ "\u008c\u0001\u008c\u0001\u008c\u0001\u008d\u0001\u008d\u0001\u008d\u0001"+ @@ -584,8 +585,8 @@ private boolean MVEXPAND_NAMED_OR_POSITIONAL_PARAM_sempred(RuleContext _localctx "\u00a5\u0001\u00a5\u0001\u00a5\u0001\u00a5\u0001\u00a6\u0001\u00a6\u0001"+ "\u00a6\u0001\u00a6\u0001\u00a7\u0001\u00a7\u0001\u00a7\u0001\u00a7\u0001"+ "\u00a7\u0001\u00a8\u0001\u00a8\u0001\u00a8\u0001\u00a8\u0001\u00a9\u0001"+ - "\u00a9\u0001\u00a9\u0001\u00a9\u0001\u00a9\u0004\u00a9\u0578\b\u00a9\u000b"+ - "\u00a9\f\u00a9\u0579\u0001\u00aa\u0001\u00aa\u0001\u00aa\u0001\u00aa\u0001"+ + "\u00a9\u0001\u00a9\u0001\u00a9\u0001\u00a9\u0004\u00a9\u057e\b\u00a9\u000b"+ + "\u00a9\f\u00a9\u057f\u0001\u00aa\u0001\u00aa\u0001\u00aa\u0001\u00aa\u0001"+ "\u00ab\u0001\u00ab\u0001\u00ab\u0001\u00ab\u0001\u00ac\u0001\u00ac\u0001"+ "\u00ac\u0001\u00ac\u0001\u00ad\u0001\u00ad\u0001\u00ad\u0001\u00ad\u0001"+ "\u00ad\u0001\u00ae\u0001\u00ae\u0001\u00ae\u0001\u00ae\u0001\u00af\u0001"+ @@ -608,19 +609,21 @@ private boolean MVEXPAND_NAMED_OR_POSITIONAL_PARAM_sempred(RuleContext _localctx "\u00c4\u0001\u00c4\u0001\u00c5\u0001\u00c5\u0001\u00c5\u0001\u00c5\u0001"+ "\u00c6\u0001\u00c6\u0001\u00c6\u0001\u00c6\u0001\u00c7\u0001\u00c7\u0001"+ "\u00c7\u0001\u00c7\u0001\u00c8\u0001\u00c8\u0001\u00c8\u0001\u00c8\u0001"+ - "\u00c8\u0001\u00c9\u0001\u00c9\u0001\u00c9\u0001\u00c9\u0001\u00c9\u0001"+ - "\u00c9\u0001\u00ca\u0001\u00ca\u0001\u00ca\u0001\u00ca\u0001\u00ca\u0001"+ - "\u00ca\u0001\u00cb\u0001\u00cb\u0001\u00cb\u0001\u00cb\u0001\u00cc\u0001"+ - "\u00cc\u0001\u00cc\u0001\u00cc\u0001\u00cd\u0001\u00cd\u0001\u00cd\u0001"+ - "\u00cd\u0001\u00ce\u0001\u00ce\u0001\u00ce\u0001\u00ce\u0001\u00ce\u0001"+ - "\u00ce\u0001\u00cf\u0001\u00cf\u0001\u00cf\u0001\u00cf\u0001\u00cf\u0001"+ - "\u00cf\u0001\u00d0\u0001\u00d0\u0001\u00d0\u0001\u00d0\u0001\u00d1\u0001"+ - "\u00d1\u0001\u00d1\u0001\u00d1\u0001\u00d2\u0001\u00d2\u0001\u00d2\u0001"+ - "\u00d2\u0001\u00d3\u0001\u00d3\u0001\u00d3\u0001\u00d3\u0001\u00d3\u0001"+ - "\u00d3\u0001\u00d4\u0001\u00d4\u0001\u00d4\u0001\u00d4\u0001\u00d4\u0001"+ - "\u00d4\u0001\u00d5\u0001\u00d5\u0001\u00d5\u0001\u00d5\u0001\u00d5\u0001"+ - "\u00d5\u0001\u00d6\u0001\u00d6\u0001\u00d6\u0001\u00d6\u0001\u00d6\u0002"+ - "\u02af\u02f4\u0000\u00d7\u0010\u0001\u0012\u0002\u0014\u0003\u0016\u0004"+ + "\u00c9\u0001\u00c9\u0001\u00c9\u0001\u00c9\u0001\u00ca\u0001\u00ca\u0001"+ + "\u00ca\u0001\u00ca\u0001\u00cb\u0001\u00cb\u0001\u00cb\u0001\u00cb\u0001"+ + "\u00cb\u0001\u00cc\u0001\u00cc\u0001\u00cc\u0001\u00cc\u0001\u00cc\u0001"+ + "\u00cc\u0001\u00cd\u0001\u00cd\u0001\u00cd\u0001\u00cd\u0001\u00cd\u0001"+ + "\u00cd\u0001\u00ce\u0001\u00ce\u0001\u00ce\u0001\u00ce\u0001\u00cf\u0001"+ + "\u00cf\u0001\u00cf\u0001\u00cf\u0001\u00d0\u0001\u00d0\u0001\u00d0\u0001"+ + "\u00d0\u0001\u00d1\u0001\u00d1\u0001\u00d1\u0001\u00d1\u0001\u00d1\u0001"+ + "\u00d1\u0001\u00d2\u0001\u00d2\u0001\u00d2\u0001\u00d2\u0001\u00d2\u0001"+ + "\u00d2\u0001\u00d3\u0001\u00d3\u0001\u00d3\u0001\u00d3\u0001\u00d4\u0001"+ + "\u00d4\u0001\u00d4\u0001\u00d4\u0001\u00d5\u0001\u00d5\u0001\u00d5\u0001"+ + "\u00d5\u0001\u00d6\u0001\u00d6\u0001\u00d6\u0001\u00d6\u0001\u00d6\u0001"+ + "\u00d6\u0001\u00d7\u0001\u00d7\u0001\u00d7\u0001\u00d7\u0001\u00d7\u0001"+ + "\u00d7\u0001\u00d8\u0001\u00d8\u0001\u00d8\u0001\u00d8\u0001\u00d8\u0001"+ + "\u00d8\u0001\u00d9\u0001\u00d9\u0001\u00d9\u0001\u00d9\u0001\u00d9\u0002"+ + "\u02b5\u02fa\u0000\u00da\u0010\u0001\u0012\u0002\u0014\u0003\u0016\u0004"+ "\u0018\u0005\u001a\u0006\u001c\u0007\u001e\b \t\"\n$\u000b&\f(\r*\u000e"+ ",\u000f.\u00100\u00112\u00124\u00136\u00148\u0015:\u0016<\u0017>\u0018"+ "@\u0019B\u001aD\u001bF\u001cH\u001dJ\u0000L\u0000N\u0000P\u0000R\u0000"+ @@ -644,809 +647,818 @@ private boolean MVEXPAND_NAMED_OR_POSITIONAL_PARAM_sempred(RuleContext _localctx "\u0172\u0000\u0174\u0000\u0176\u0000\u0178s\u017at\u017cu\u017e\u0000"+ "\u0180\u0000\u0182\u0000\u0184\u0000\u0186v\u0188w\u018ax\u018c\u0000"+ "\u018e\u0000\u0190\u0000\u0192\u0000\u0194y\u0196\u0000\u0198\u0000\u019a"+ - "z\u019c{\u019e|\u01a0\u0000\u01a2\u0000\u01a4\u0000\u01a6}\u01a8~\u01aa"+ - "\u007f\u01ac\u0000\u01ae\u0000\u01b0\u0080\u01b2\u0081\u01b4\u0082\u01b6"+ - "\u0000\u01b8\u0000\u01ba\u0000\u01bc\u0000\u0010\u0000\u0001\u0002\u0003"+ - "\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f$\u0002\u0000DDdd"+ - "\u0002\u0000IIii\u0002\u0000SSss\u0002\u0000EEee\u0002\u0000CCcc\u0002"+ - "\u0000TTtt\u0002\u0000RRrr\u0002\u0000OOoo\u0002\u0000PPpp\u0002\u0000"+ - "NNnn\u0002\u0000HHhh\u0002\u0000VVvv\u0002\u0000AAaa\u0002\u0000LLll\u0002"+ - "\u0000XXxx\u0002\u0000FFff\u0002\u0000MMmm\u0002\u0000GGgg\u0002\u0000"+ - "KKkk\u0002\u0000WWww\u0002\u0000UUuu\u0002\u0000JJjj\u0006\u0000\t\n\r"+ - "\r //[[]]\u0002\u0000\n\n\r\r\u0003\u0000\t\n\r\r \u0001\u000009\u0002"+ - "\u0000AZaz\b\u0000\"\"NNRRTT\\\\nnrrtt\u0004\u0000\n\n\r\r\"\"\\\\\u0002"+ - "\u0000++--\u0001\u0000``\u0002\u0000BBbb\u0002\u0000YYyy\u000b\u0000\t"+ - "\n\r\r \"\",,//::==[[]]||\u0002\u0000**//\u000b\u0000\t\n\r\r \"#,,"+ - "//::<<>?\\\\||\u0666\u0000\u0010\u0001\u0000\u0000\u0000\u0000\u0012\u0001"+ - "\u0000\u0000\u0000\u0000\u0014\u0001\u0000\u0000\u0000\u0000\u0016\u0001"+ - "\u0000\u0000\u0000\u0000\u0018\u0001\u0000\u0000\u0000\u0000\u001a\u0001"+ - "\u0000\u0000\u0000\u0000\u001c\u0001\u0000\u0000\u0000\u0000\u001e\u0001"+ - "\u0000\u0000\u0000\u0000 \u0001\u0000\u0000\u0000\u0000\"\u0001\u0000"+ - "\u0000\u0000\u0000$\u0001\u0000\u0000\u0000\u0000&\u0001\u0000\u0000\u0000"+ - "\u0000(\u0001\u0000\u0000\u0000\u0000*\u0001\u0000\u0000\u0000\u0000,"+ - "\u0001\u0000\u0000\u0000\u0000.\u0001\u0000\u0000\u0000\u00000\u0001\u0000"+ - "\u0000\u0000\u00002\u0001\u0000\u0000\u0000\u00004\u0001\u0000\u0000\u0000"+ - "\u00006\u0001\u0000\u0000\u0000\u00008\u0001\u0000\u0000\u0000\u0000:"+ - "\u0001\u0000\u0000\u0000\u0000<\u0001\u0000\u0000\u0000\u0000>\u0001\u0000"+ - "\u0000\u0000\u0000@\u0001\u0000\u0000\u0000\u0000B\u0001\u0000\u0000\u0000"+ - "\u0000D\u0001\u0000\u0000\u0000\u0000F\u0001\u0000\u0000\u0000\u0001H"+ - "\u0001\u0000\u0000\u0000\u0001^\u0001\u0000\u0000\u0000\u0001`\u0001\u0000"+ - "\u0000\u0000\u0001b\u0001\u0000\u0000\u0000\u0001d\u0001\u0000\u0000\u0000"+ - "\u0001f\u0001\u0000\u0000\u0000\u0001h\u0001\u0000\u0000\u0000\u0001j"+ - "\u0001\u0000\u0000\u0000\u0001l\u0001\u0000\u0000\u0000\u0001n\u0001\u0000"+ - "\u0000\u0000\u0001p\u0001\u0000\u0000\u0000\u0001r\u0001\u0000\u0000\u0000"+ - "\u0001t\u0001\u0000\u0000\u0000\u0001v\u0001\u0000\u0000\u0000\u0001x"+ - "\u0001\u0000\u0000\u0000\u0001z\u0001\u0000\u0000\u0000\u0001|\u0001\u0000"+ - "\u0000\u0000\u0001~\u0001\u0000\u0000\u0000\u0001\u0080\u0001\u0000\u0000"+ - "\u0000\u0001\u0082\u0001\u0000\u0000\u0000\u0001\u0084\u0001\u0000\u0000"+ - "\u0000\u0001\u0086\u0001\u0000\u0000\u0000\u0001\u0088\u0001\u0000\u0000"+ - "\u0000\u0001\u008a\u0001\u0000\u0000\u0000\u0001\u008c\u0001\u0000\u0000"+ - "\u0000\u0001\u008e\u0001\u0000\u0000\u0000\u0001\u0090\u0001\u0000\u0000"+ - "\u0000\u0001\u0092\u0001\u0000\u0000\u0000\u0001\u0094\u0001\u0000\u0000"+ - "\u0000\u0001\u0096\u0001\u0000\u0000\u0000\u0001\u0098\u0001\u0000\u0000"+ - "\u0000\u0001\u009a\u0001\u0000\u0000\u0000\u0001\u009c\u0001\u0000\u0000"+ - "\u0000\u0001\u009e\u0001\u0000\u0000\u0000\u0001\u00a0\u0001\u0000\u0000"+ - "\u0000\u0001\u00a2\u0001\u0000\u0000\u0000\u0001\u00a4\u0001\u0000\u0000"+ - "\u0000\u0001\u00a6\u0001\u0000\u0000\u0000\u0001\u00a8\u0001\u0000\u0000"+ - "\u0000\u0001\u00aa\u0001\u0000\u0000\u0000\u0001\u00ac\u0001\u0000\u0000"+ - "\u0000\u0001\u00ae\u0001\u0000\u0000\u0000\u0001\u00b0\u0001\u0000\u0000"+ - "\u0000\u0001\u00b2\u0001\u0000\u0000\u0000\u0001\u00b4\u0001\u0000\u0000"+ - "\u0000\u0001\u00b6\u0001\u0000\u0000\u0000\u0001\u00b8\u0001\u0000\u0000"+ - "\u0000\u0001\u00bc\u0001\u0000\u0000\u0000\u0001\u00be\u0001\u0000\u0000"+ - "\u0000\u0001\u00c0\u0001\u0000\u0000\u0000\u0001\u00c2\u0001\u0000\u0000"+ - "\u0000\u0002\u00c4\u0001\u0000\u0000\u0000\u0002\u00c6\u0001\u0000\u0000"+ - "\u0000\u0002\u00c8\u0001\u0000\u0000\u0000\u0002\u00ca\u0001\u0000\u0000"+ - "\u0000\u0002\u00cc\u0001\u0000\u0000\u0000\u0003\u00ce\u0001\u0000\u0000"+ - "\u0000\u0003\u00d0\u0001\u0000\u0000\u0000\u0003\u00d2\u0001\u0000\u0000"+ - "\u0000\u0003\u00d4\u0001\u0000\u0000\u0000\u0003\u00d6\u0001\u0000\u0000"+ - "\u0000\u0003\u00d8\u0001\u0000\u0000\u0000\u0003\u00da\u0001\u0000\u0000"+ - "\u0000\u0003\u00de\u0001\u0000\u0000\u0000\u0003\u00e0\u0001\u0000\u0000"+ - "\u0000\u0003\u00e2\u0001\u0000\u0000\u0000\u0003\u00e4\u0001\u0000\u0000"+ - "\u0000\u0003\u00e6\u0001\u0000\u0000\u0000\u0003\u00e8\u0001\u0000\u0000"+ - "\u0000\u0004\u00ea\u0001\u0000\u0000\u0000\u0004\u00ec\u0001\u0000\u0000"+ - "\u0000\u0004\u00ee\u0001\u0000\u0000\u0000\u0004\u00f0\u0001\u0000\u0000"+ - "\u0000\u0004\u00f2\u0001\u0000\u0000\u0000\u0004\u00f8\u0001\u0000\u0000"+ - "\u0000\u0004\u00fa\u0001\u0000\u0000\u0000\u0004\u00fc\u0001\u0000\u0000"+ - "\u0000\u0004\u00fe\u0001\u0000\u0000\u0000\u0005\u0100\u0001\u0000\u0000"+ - "\u0000\u0005\u0102\u0001\u0000\u0000\u0000\u0005\u0104\u0001\u0000\u0000"+ - "\u0000\u0005\u0106\u0001\u0000\u0000\u0000\u0005\u0108\u0001\u0000\u0000"+ - "\u0000\u0005\u010a\u0001\u0000\u0000\u0000\u0005\u010c\u0001\u0000\u0000"+ - "\u0000\u0005\u010e\u0001\u0000\u0000\u0000\u0005\u0110\u0001\u0000\u0000"+ - "\u0000\u0005\u0112\u0001\u0000\u0000\u0000\u0005\u0114\u0001\u0000\u0000"+ - "\u0000\u0006\u0116\u0001\u0000\u0000\u0000\u0006\u0118\u0001\u0000\u0000"+ - "\u0000\u0006\u011a\u0001\u0000\u0000\u0000\u0006\u011c\u0001\u0000\u0000"+ - "\u0000\u0006\u0120\u0001\u0000\u0000\u0000\u0006\u0122\u0001\u0000\u0000"+ - "\u0000\u0006\u0124\u0001\u0000\u0000\u0000\u0006\u0126\u0001\u0000\u0000"+ - "\u0000\u0006\u0128\u0001\u0000\u0000\u0000\u0007\u012a\u0001\u0000\u0000"+ - "\u0000\u0007\u012c\u0001\u0000\u0000\u0000\u0007\u012e\u0001\u0000\u0000"+ - "\u0000\u0007\u0130\u0001\u0000\u0000\u0000\u0007\u0132\u0001\u0000\u0000"+ - "\u0000\u0007\u0134\u0001\u0000\u0000\u0000\u0007\u0136\u0001\u0000\u0000"+ - "\u0000\u0007\u0138\u0001\u0000\u0000\u0000\u0007\u013a\u0001\u0000\u0000"+ - "\u0000\u0007\u013c\u0001\u0000\u0000\u0000\u0007\u013e\u0001\u0000\u0000"+ - "\u0000\u0007\u0140\u0001\u0000\u0000\u0000\b\u0142\u0001\u0000\u0000\u0000"+ - "\b\u0144\u0001\u0000\u0000\u0000\b\u0146\u0001\u0000\u0000\u0000\b\u0148"+ - "\u0001\u0000\u0000\u0000\b\u014a\u0001\u0000\u0000\u0000\b\u014c\u0001"+ - "\u0000\u0000\u0000\b\u014e\u0001\u0000\u0000\u0000\b\u0150\u0001\u0000"+ - "\u0000\u0000\b\u0152\u0001\u0000\u0000\u0000\t\u0154\u0001\u0000\u0000"+ - "\u0000\t\u0156\u0001\u0000\u0000\u0000\t\u0158\u0001\u0000\u0000\u0000"+ - "\t\u015a\u0001\u0000\u0000\u0000\t\u015c\u0001\u0000\u0000\u0000\n\u015e"+ - "\u0001\u0000\u0000\u0000\n\u0160\u0001\u0000\u0000\u0000\n\u0162\u0001"+ - "\u0000\u0000\u0000\n\u0164\u0001\u0000\u0000\u0000\n\u0166\u0001\u0000"+ - "\u0000\u0000\n\u0168\u0001\u0000\u0000\u0000\u000b\u016a\u0001\u0000\u0000"+ - "\u0000\u000b\u016c\u0001\u0000\u0000\u0000\u000b\u016e\u0001\u0000\u0000"+ - "\u0000\u000b\u0170\u0001\u0000\u0000\u0000\u000b\u0172\u0001\u0000\u0000"+ - "\u0000\u000b\u0174\u0001\u0000\u0000\u0000\u000b\u0176\u0001\u0000\u0000"+ - "\u0000\u000b\u0178\u0001\u0000\u0000\u0000\u000b\u017a\u0001\u0000\u0000"+ - "\u0000\u000b\u017c\u0001\u0000\u0000\u0000\f\u017e\u0001\u0000\u0000\u0000"+ - "\f\u0180\u0001\u0000\u0000\u0000\f\u0182\u0001\u0000\u0000\u0000\f\u0184"+ - "\u0001\u0000\u0000\u0000\f\u0186\u0001\u0000\u0000\u0000\f\u0188\u0001"+ - "\u0000\u0000\u0000\f\u018a\u0001\u0000\u0000\u0000\r\u018c\u0001\u0000"+ - "\u0000\u0000\r\u018e\u0001\u0000\u0000\u0000\r\u0190\u0001\u0000\u0000"+ - "\u0000\r\u0192\u0001\u0000\u0000\u0000\r\u0194\u0001\u0000\u0000\u0000"+ - "\r\u0196\u0001\u0000\u0000\u0000\r\u0198\u0001\u0000\u0000\u0000\r\u019a"+ - "\u0001\u0000\u0000\u0000\r\u019c\u0001\u0000\u0000\u0000\r\u019e\u0001"+ - "\u0000\u0000\u0000\u000e\u01a0\u0001\u0000\u0000\u0000\u000e\u01a2\u0001"+ - "\u0000\u0000\u0000\u000e\u01a4\u0001\u0000\u0000\u0000\u000e\u01a6\u0001"+ - "\u0000\u0000\u0000\u000e\u01a8\u0001\u0000\u0000\u0000\u000e\u01aa\u0001"+ - "\u0000\u0000\u0000\u000f\u01ac\u0001\u0000\u0000\u0000\u000f\u01ae\u0001"+ - "\u0000\u0000\u0000\u000f\u01b0\u0001\u0000\u0000\u0000\u000f\u01b2\u0001"+ - "\u0000\u0000\u0000\u000f\u01b4\u0001\u0000\u0000\u0000\u000f\u01b6\u0001"+ - "\u0000\u0000\u0000\u000f\u01b8\u0001\u0000\u0000\u0000\u000f\u01ba\u0001"+ - "\u0000\u0000\u0000\u000f\u01bc\u0001\u0000\u0000\u0000\u0010\u01be\u0001"+ - "\u0000\u0000\u0000\u0012\u01c8\u0001\u0000\u0000\u0000\u0014\u01cf\u0001"+ - "\u0000\u0000\u0000\u0016\u01d8\u0001\u0000\u0000\u0000\u0018\u01df\u0001"+ - "\u0000\u0000\u0000\u001a\u01e9\u0001\u0000\u0000\u0000\u001c\u01f0\u0001"+ - "\u0000\u0000\u0000\u001e\u01f7\u0001\u0000\u0000\u0000 \u01fe\u0001\u0000"+ - "\u0000\u0000\"\u0206\u0001\u0000\u0000\u0000$\u0212\u0001\u0000\u0000"+ - "\u0000&\u021b\u0001\u0000\u0000\u0000(\u0221\u0001\u0000\u0000\u0000*"+ - "\u0228\u0001\u0000\u0000\u0000,\u022f\u0001\u0000\u0000\u0000.\u0237\u0001"+ - "\u0000\u0000\u00000\u023f\u0001\u0000\u0000\u00002\u024e\u0001\u0000\u0000"+ - "\u00004\u025a\u0001\u0000\u0000\u00006\u0265\u0001\u0000\u0000\u00008"+ - "\u026d\u0001\u0000\u0000\u0000:\u0275\u0001\u0000\u0000\u0000<\u027d\u0001"+ - "\u0000\u0000\u0000>\u0286\u0001\u0000\u0000\u0000@\u0291\u0001\u0000\u0000"+ - "\u0000B\u0297\u0001\u0000\u0000\u0000D\u02a8\u0001\u0000\u0000\u0000F"+ - "\u02b8\u0001\u0000\u0000\u0000H\u02be\u0001\u0000\u0000\u0000J\u02c2\u0001"+ - "\u0000\u0000\u0000L\u02c4\u0001\u0000\u0000\u0000N\u02c6\u0001\u0000\u0000"+ - "\u0000P\u02c9\u0001\u0000\u0000\u0000R\u02cb\u0001\u0000\u0000\u0000T"+ - "\u02d4\u0001\u0000\u0000\u0000V\u02d6\u0001\u0000\u0000\u0000X\u02db\u0001"+ - "\u0000\u0000\u0000Z\u02dd\u0001\u0000\u0000\u0000\\\u02e2\u0001\u0000"+ - "\u0000\u0000^\u0301\u0001\u0000\u0000\u0000`\u0304\u0001\u0000\u0000\u0000"+ - "b\u0332\u0001\u0000\u0000\u0000d\u0334\u0001\u0000\u0000\u0000f\u0337"+ - "\u0001\u0000\u0000\u0000h\u033b\u0001\u0000\u0000\u0000j\u033f\u0001\u0000"+ - "\u0000\u0000l\u0341\u0001\u0000\u0000\u0000n\u0344\u0001\u0000\u0000\u0000"+ - "p\u0346\u0001\u0000\u0000\u0000r\u0348\u0001\u0000\u0000\u0000t\u034d"+ - "\u0001\u0000\u0000\u0000v\u034f\u0001\u0000\u0000\u0000x\u0355\u0001\u0000"+ - "\u0000\u0000z\u035b\u0001\u0000\u0000\u0000|\u035e\u0001\u0000\u0000\u0000"+ - "~\u0361\u0001\u0000\u0000\u0000\u0080\u0366\u0001\u0000\u0000\u0000\u0082"+ - "\u036b\u0001\u0000\u0000\u0000\u0084\u036d\u0001\u0000\u0000\u0000\u0086"+ - "\u0371\u0001\u0000\u0000\u0000\u0088\u0376\u0001\u0000\u0000\u0000\u008a"+ - "\u037c\u0001\u0000\u0000\u0000\u008c\u037f\u0001\u0000\u0000\u0000\u008e"+ - "\u0381\u0001\u0000\u0000\u0000\u0090\u0387\u0001\u0000\u0000\u0000\u0092"+ - "\u0389\u0001\u0000\u0000\u0000\u0094\u038e\u0001\u0000\u0000\u0000\u0096"+ - "\u0391\u0001\u0000\u0000\u0000\u0098\u0394\u0001\u0000\u0000\u0000\u009a"+ - "\u0397\u0001\u0000\u0000\u0000\u009c\u0399\u0001\u0000\u0000\u0000\u009e"+ - "\u039c\u0001\u0000\u0000\u0000\u00a0\u039e\u0001\u0000\u0000\u0000\u00a2"+ - "\u03a1\u0001\u0000\u0000\u0000\u00a4\u03a3\u0001\u0000\u0000\u0000\u00a6"+ - "\u03a5\u0001\u0000\u0000\u0000\u00a8\u03a7\u0001\u0000\u0000\u0000\u00aa"+ - "\u03a9\u0001\u0000\u0000\u0000\u00ac\u03ab\u0001\u0000\u0000\u0000\u00ae"+ - "\u03ae\u0001\u0000\u0000\u0000\u00b0\u03b1\u0001\u0000\u0000\u0000\u00b2"+ - "\u03c6\u0001\u0000\u0000\u0000\u00b4\u03c8\u0001\u0000\u0000\u0000\u00b6"+ - "\u03cd\u0001\u0000\u0000\u0000\u00b8\u03e2\u0001\u0000\u0000\u0000\u00ba"+ - "\u03e4\u0001\u0000\u0000\u0000\u00bc\u03ec\u0001\u0000\u0000\u0000\u00be"+ - "\u03ee\u0001\u0000\u0000\u0000\u00c0\u03f2\u0001\u0000\u0000\u0000\u00c2"+ - "\u03f6\u0001\u0000\u0000\u0000\u00c4\u03fa\u0001\u0000\u0000\u0000\u00c6"+ - "\u03ff\u0001\u0000\u0000\u0000\u00c8\u0404\u0001\u0000\u0000\u0000\u00ca"+ - "\u0408\u0001\u0000\u0000\u0000\u00cc\u040c\u0001\u0000\u0000\u0000\u00ce"+ - "\u0410\u0001\u0000\u0000\u0000\u00d0\u0415\u0001\u0000\u0000\u0000\u00d2"+ - "\u0419\u0001\u0000\u0000\u0000\u00d4\u041d\u0001\u0000\u0000\u0000\u00d6"+ - "\u0421\u0001\u0000\u0000\u0000\u00d8\u0425\u0001\u0000\u0000\u0000\u00da"+ - "\u0429\u0001\u0000\u0000\u0000\u00dc\u0435\u0001\u0000\u0000\u0000\u00de"+ - "\u0438\u0001\u0000\u0000\u0000\u00e0\u043c\u0001\u0000\u0000\u0000\u00e2"+ - "\u0440\u0001\u0000\u0000\u0000\u00e4\u0444\u0001\u0000\u0000\u0000\u00e6"+ - "\u0448\u0001\u0000\u0000\u0000\u00e8\u044c\u0001\u0000\u0000\u0000\u00ea"+ - "\u0450\u0001\u0000\u0000\u0000\u00ec\u0455\u0001\u0000\u0000\u0000\u00ee"+ - "\u0459\u0001\u0000\u0000\u0000\u00f0\u045d\u0001\u0000\u0000\u0000\u00f2"+ - "\u0462\u0001\u0000\u0000\u0000\u00f4\u046b\u0001\u0000\u0000\u0000\u00f6"+ - "\u0480\u0001\u0000\u0000\u0000\u00f8\u0484\u0001\u0000\u0000\u0000\u00fa"+ - "\u0488\u0001\u0000\u0000\u0000\u00fc\u048c\u0001\u0000\u0000\u0000\u00fe"+ - "\u0490\u0001\u0000\u0000\u0000\u0100\u0494\u0001\u0000\u0000\u0000\u0102"+ - "\u0499\u0001\u0000\u0000\u0000\u0104\u049d\u0001\u0000\u0000\u0000\u0106"+ - "\u04a1\u0001\u0000\u0000\u0000\u0108\u04a5\u0001\u0000\u0000\u0000\u010a"+ - "\u04aa\u0001\u0000\u0000\u0000\u010c\u04af\u0001\u0000\u0000\u0000\u010e"+ - "\u04b2\u0001\u0000\u0000\u0000\u0110\u04b6\u0001\u0000\u0000\u0000\u0112"+ - "\u04ba\u0001\u0000\u0000\u0000\u0114\u04be\u0001\u0000\u0000\u0000\u0116"+ - "\u04c2\u0001\u0000\u0000\u0000\u0118\u04c7\u0001\u0000\u0000\u0000\u011a"+ - "\u04cc\u0001\u0000\u0000\u0000\u011c\u04d1\u0001\u0000\u0000\u0000\u011e"+ - "\u04d8\u0001\u0000\u0000\u0000\u0120\u04e1\u0001\u0000\u0000\u0000\u0122"+ - "\u04e8\u0001\u0000\u0000\u0000\u0124\u04ec\u0001\u0000\u0000\u0000\u0126"+ - "\u04f0\u0001\u0000\u0000\u0000\u0128\u04f4\u0001\u0000\u0000\u0000\u012a"+ - "\u04f8\u0001\u0000\u0000\u0000\u012c\u04fe\u0001\u0000\u0000\u0000\u012e"+ - "\u0502\u0001\u0000\u0000\u0000\u0130\u0506\u0001\u0000\u0000\u0000\u0132"+ - "\u050a\u0001\u0000\u0000\u0000\u0134\u050e\u0001\u0000\u0000\u0000\u0136"+ - "\u0512\u0001\u0000\u0000\u0000\u0138\u0516\u0001\u0000\u0000\u0000\u013a"+ - "\u051b\u0001\u0000\u0000\u0000\u013c\u0520\u0001\u0000\u0000\u0000\u013e"+ - "\u0524\u0001\u0000\u0000\u0000\u0140\u0528\u0001\u0000\u0000\u0000\u0142"+ - "\u052c\u0001\u0000\u0000\u0000\u0144\u0531\u0001\u0000\u0000\u0000\u0146"+ - "\u0535\u0001\u0000\u0000\u0000\u0148\u053a\u0001\u0000\u0000\u0000\u014a"+ - "\u053f\u0001\u0000\u0000\u0000\u014c\u0543\u0001\u0000\u0000\u0000\u014e"+ - "\u0547\u0001\u0000\u0000\u0000\u0150\u054b\u0001\u0000\u0000\u0000\u0152"+ - "\u054f\u0001\u0000\u0000\u0000\u0154\u0553\u0001\u0000\u0000\u0000\u0156"+ - "\u0558\u0001\u0000\u0000\u0000\u0158\u055d\u0001\u0000\u0000\u0000\u015a"+ - "\u0561\u0001\u0000\u0000\u0000\u015c\u0565\u0001\u0000\u0000\u0000\u015e"+ - "\u0569\u0001\u0000\u0000\u0000\u0160\u056e\u0001\u0000\u0000\u0000\u0162"+ - "\u0577\u0001\u0000\u0000\u0000\u0164\u057b\u0001\u0000\u0000\u0000\u0166"+ - "\u057f\u0001\u0000\u0000\u0000\u0168\u0583\u0001\u0000\u0000\u0000\u016a"+ - "\u0587\u0001\u0000\u0000\u0000\u016c\u058c\u0001\u0000\u0000\u0000\u016e"+ - "\u0590\u0001\u0000\u0000\u0000\u0170\u0594\u0001\u0000\u0000\u0000\u0172"+ - "\u0598\u0001\u0000\u0000\u0000\u0174\u059d\u0001\u0000\u0000\u0000\u0176"+ - "\u05a1\u0001\u0000\u0000\u0000\u0178\u05a5\u0001\u0000\u0000\u0000\u017a"+ - "\u05a9\u0001\u0000\u0000\u0000\u017c\u05ad\u0001\u0000\u0000\u0000\u017e"+ - "\u05b1\u0001\u0000\u0000\u0000\u0180\u05b7\u0001\u0000\u0000\u0000\u0182"+ - "\u05bb\u0001\u0000\u0000\u0000\u0184\u05bf\u0001\u0000\u0000\u0000\u0186"+ - "\u05c3\u0001\u0000\u0000\u0000\u0188\u05c7\u0001\u0000\u0000\u0000\u018a"+ - "\u05cb\u0001\u0000\u0000\u0000\u018c\u05cf\u0001\u0000\u0000\u0000\u018e"+ - "\u05d4\u0001\u0000\u0000\u0000\u0190\u05d8\u0001\u0000\u0000\u0000\u0192"+ - "\u05dc\u0001\u0000\u0000\u0000\u0194\u05e2\u0001\u0000\u0000\u0000\u0196"+ - "\u05eb\u0001\u0000\u0000\u0000\u0198\u05ef\u0001\u0000\u0000\u0000\u019a"+ - "\u05f3\u0001\u0000\u0000\u0000\u019c\u05f7\u0001\u0000\u0000\u0000\u019e"+ - "\u05fb\u0001\u0000\u0000\u0000\u01a0\u05ff\u0001\u0000\u0000\u0000\u01a2"+ - "\u0604\u0001\u0000\u0000\u0000\u01a4\u060a\u0001\u0000\u0000\u0000\u01a6"+ - "\u0610\u0001\u0000\u0000\u0000\u01a8\u0614\u0001\u0000\u0000\u0000\u01aa"+ - "\u0618\u0001\u0000\u0000\u0000\u01ac\u061c\u0001\u0000\u0000\u0000\u01ae"+ - "\u0622\u0001\u0000\u0000\u0000\u01b0\u0628\u0001\u0000\u0000\u0000\u01b2"+ - "\u062c\u0001\u0000\u0000\u0000\u01b4\u0630\u0001\u0000\u0000\u0000\u01b6"+ - "\u0634\u0001\u0000\u0000\u0000\u01b8\u063a\u0001\u0000\u0000\u0000\u01ba"+ - "\u0640\u0001\u0000\u0000\u0000\u01bc\u0646\u0001\u0000\u0000\u0000\u01be"+ - "\u01bf\u0007\u0000\u0000\u0000\u01bf\u01c0\u0007\u0001\u0000\u0000\u01c0"+ - "\u01c1\u0007\u0002\u0000\u0000\u01c1\u01c2\u0007\u0002\u0000\u0000\u01c2"+ - "\u01c3\u0007\u0003\u0000\u0000\u01c3\u01c4\u0007\u0004\u0000\u0000\u01c4"+ - "\u01c5\u0007\u0005\u0000\u0000\u01c5\u01c6\u0001\u0000\u0000\u0000\u01c6"+ - "\u01c7\u0006\u0000\u0000\u0000\u01c7\u0011\u0001\u0000\u0000\u0000\u01c8"+ - "\u01c9\u0007\u0000\u0000\u0000\u01c9\u01ca\u0007\u0006\u0000\u0000\u01ca"+ - "\u01cb\u0007\u0007\u0000\u0000\u01cb\u01cc\u0007\b\u0000\u0000\u01cc\u01cd"+ - "\u0001\u0000\u0000\u0000\u01cd\u01ce\u0006\u0001\u0001\u0000\u01ce\u0013"+ - "\u0001\u0000\u0000\u0000\u01cf\u01d0\u0007\u0003\u0000\u0000\u01d0\u01d1"+ - "\u0007\t\u0000\u0000\u01d1\u01d2\u0007\u0006\u0000\u0000\u01d2\u01d3\u0007"+ - "\u0001\u0000\u0000\u01d3\u01d4\u0007\u0004\u0000\u0000\u01d4\u01d5\u0007"+ - "\n\u0000\u0000\u01d5\u01d6\u0001\u0000\u0000\u0000\u01d6\u01d7\u0006\u0002"+ - "\u0002\u0000\u01d7\u0015\u0001\u0000\u0000\u0000\u01d8\u01d9\u0007\u0003"+ - "\u0000\u0000\u01d9\u01da\u0007\u000b\u0000\u0000\u01da\u01db\u0007\f\u0000"+ - "\u0000\u01db\u01dc\u0007\r\u0000\u0000\u01dc\u01dd\u0001\u0000\u0000\u0000"+ - "\u01dd\u01de\u0006\u0003\u0000\u0000\u01de\u0017\u0001\u0000\u0000\u0000"+ - "\u01df\u01e0\u0007\u0003\u0000\u0000\u01e0\u01e1\u0007\u000e\u0000\u0000"+ - "\u01e1\u01e2\u0007\b\u0000\u0000\u01e2\u01e3\u0007\r\u0000\u0000\u01e3"+ - "\u01e4\u0007\f\u0000\u0000\u01e4\u01e5\u0007\u0001\u0000\u0000\u01e5\u01e6"+ - "\u0007\t\u0000\u0000\u01e6\u01e7\u0001\u0000\u0000\u0000\u01e7\u01e8\u0006"+ - "\u0004\u0003\u0000\u01e8\u0019\u0001\u0000\u0000\u0000\u01e9\u01ea\u0007"+ - "\u000f\u0000\u0000\u01ea\u01eb\u0007\u0006\u0000\u0000\u01eb\u01ec\u0007"+ - "\u0007\u0000\u0000\u01ec\u01ed\u0007\u0010\u0000\u0000\u01ed\u01ee\u0001"+ - "\u0000\u0000\u0000\u01ee\u01ef\u0006\u0005\u0004\u0000\u01ef\u001b\u0001"+ - "\u0000\u0000\u0000\u01f0\u01f1\u0007\u0011\u0000\u0000\u01f1\u01f2\u0007"+ - "\u0006\u0000\u0000\u01f2\u01f3\u0007\u0007\u0000\u0000\u01f3\u01f4\u0007"+ - "\u0012\u0000\u0000\u01f4\u01f5\u0001\u0000\u0000\u0000\u01f5\u01f6\u0006"+ - "\u0006\u0000\u0000\u01f6\u001d\u0001\u0000\u0000\u0000\u01f7\u01f8\u0007"+ - "\u0012\u0000\u0000\u01f8\u01f9\u0007\u0003\u0000\u0000\u01f9\u01fa\u0007"+ - "\u0003\u0000\u0000\u01fa\u01fb\u0007\b\u0000\u0000\u01fb\u01fc\u0001\u0000"+ - "\u0000\u0000\u01fc\u01fd\u0006\u0007\u0001\u0000\u01fd\u001f\u0001\u0000"+ - "\u0000\u0000\u01fe\u01ff\u0007\r\u0000\u0000\u01ff\u0200\u0007\u0001\u0000"+ - "\u0000\u0200\u0201\u0007\u0010\u0000\u0000\u0201\u0202\u0007\u0001\u0000"+ - "\u0000\u0202\u0203\u0007\u0005\u0000\u0000\u0203\u0204\u0001\u0000\u0000"+ - "\u0000\u0204\u0205\u0006\b\u0000\u0000\u0205!\u0001\u0000\u0000\u0000"+ - "\u0206\u0207\u0007\u0010\u0000\u0000\u0207\u0208\u0007\u000b\u0000\u0000"+ - "\u0208\u0209\u0005_\u0000\u0000\u0209\u020a\u0007\u0003\u0000\u0000\u020a"+ - "\u020b\u0007\u000e\u0000\u0000\u020b\u020c\u0007\b\u0000\u0000\u020c\u020d"+ - "\u0007\f\u0000\u0000\u020d\u020e\u0007\t\u0000\u0000\u020e\u020f\u0007"+ - "\u0000\u0000\u0000\u020f\u0210\u0001\u0000\u0000\u0000\u0210\u0211\u0006"+ - "\t\u0005\u0000\u0211#\u0001\u0000\u0000\u0000\u0212\u0213\u0007\u0006"+ - "\u0000\u0000\u0213\u0214\u0007\u0003\u0000\u0000\u0214\u0215\u0007\t\u0000"+ - "\u0000\u0215\u0216\u0007\f\u0000\u0000\u0216\u0217\u0007\u0010\u0000\u0000"+ - "\u0217\u0218\u0007\u0003\u0000\u0000\u0218\u0219\u0001\u0000\u0000\u0000"+ - "\u0219\u021a\u0006\n\u0006\u0000\u021a%\u0001\u0000\u0000\u0000\u021b"+ - "\u021c\u0007\u0006\u0000\u0000\u021c\u021d\u0007\u0007\u0000\u0000\u021d"+ - "\u021e\u0007\u0013\u0000\u0000\u021e\u021f\u0001\u0000\u0000\u0000\u021f"+ - "\u0220\u0006\u000b\u0000\u0000\u0220\'\u0001\u0000\u0000\u0000\u0221\u0222"+ - "\u0007\u0002\u0000\u0000\u0222\u0223\u0007\n\u0000\u0000\u0223\u0224\u0007"+ - "\u0007\u0000\u0000\u0224\u0225\u0007\u0013\u0000\u0000\u0225\u0226\u0001"+ - "\u0000\u0000\u0000\u0226\u0227\u0006\f\u0007\u0000\u0227)\u0001\u0000"+ - "\u0000\u0000\u0228\u0229\u0007\u0002\u0000\u0000\u0229\u022a\u0007\u0007"+ - "\u0000\u0000\u022a\u022b\u0007\u0006\u0000\u0000\u022b\u022c\u0007\u0005"+ - "\u0000\u0000\u022c\u022d\u0001\u0000\u0000\u0000\u022d\u022e\u0006\r\u0000"+ - "\u0000\u022e+\u0001\u0000\u0000\u0000\u022f\u0230\u0007\u0002\u0000\u0000"+ - "\u0230\u0231\u0007\u0005\u0000\u0000\u0231\u0232\u0007\f\u0000\u0000\u0232"+ - "\u0233\u0007\u0005\u0000\u0000\u0233\u0234\u0007\u0002\u0000\u0000\u0234"+ - "\u0235\u0001\u0000\u0000\u0000\u0235\u0236\u0006\u000e\u0000\u0000\u0236"+ - "-\u0001\u0000\u0000\u0000\u0237\u0238\u0007\u0013\u0000\u0000\u0238\u0239"+ - "\u0007\n\u0000\u0000\u0239\u023a\u0007\u0003\u0000\u0000\u023a\u023b\u0007"+ - "\u0006\u0000\u0000\u023b\u023c\u0007\u0003\u0000\u0000\u023c\u023d\u0001"+ - "\u0000\u0000\u0000\u023d\u023e\u0006\u000f\u0000\u0000\u023e/\u0001\u0000"+ - "\u0000\u0000\u023f\u0240\u0004\u0010\u0000\u0000\u0240\u0241\u0007\u0001"+ - "\u0000\u0000\u0241\u0242\u0007\t\u0000\u0000\u0242\u0243\u0007\r\u0000"+ - "\u0000\u0243\u0244\u0007\u0001\u0000\u0000\u0244\u0245\u0007\t\u0000\u0000"+ - "\u0245\u0246\u0007\u0003\u0000\u0000\u0246\u0247\u0007\u0002\u0000\u0000"+ - "\u0247\u0248\u0007\u0005\u0000\u0000\u0248\u0249\u0007\f\u0000\u0000\u0249"+ - "\u024a\u0007\u0005\u0000\u0000\u024a\u024b\u0007\u0002\u0000\u0000\u024b"+ - "\u024c\u0001\u0000\u0000\u0000\u024c\u024d\u0006\u0010\u0000\u0000\u024d"+ - "1\u0001\u0000\u0000\u0000\u024e\u024f\u0004\u0011\u0001\u0000\u024f\u0250"+ - "\u0007\r\u0000\u0000\u0250\u0251\u0007\u0007\u0000\u0000\u0251\u0252\u0007"+ - "\u0007\u0000\u0000\u0252\u0253\u0007\u0012\u0000\u0000\u0253\u0254\u0007"+ - "\u0014\u0000\u0000\u0254\u0255\u0007\b\u0000\u0000\u0255\u0256\u0005_"+ - "\u0000\u0000\u0256\u0257\u0005\u8001\uf414\u0000\u0000\u0257\u0258\u0001"+ - "\u0000\u0000\u0000\u0258\u0259\u0006\u0011\b\u0000\u02593\u0001\u0000"+ - "\u0000\u0000\u025a\u025b\u0004\u0012\u0002\u0000\u025b\u025c\u0007\u0010"+ - "\u0000\u0000\u025c\u025d\u0007\u0003\u0000\u0000\u025d\u025e\u0007\u0005"+ - "\u0000\u0000\u025e\u025f\u0007\u0006\u0000\u0000\u025f\u0260\u0007\u0001"+ - "\u0000\u0000\u0260\u0261\u0007\u0004\u0000\u0000\u0261\u0262\u0007\u0002"+ - "\u0000\u0000\u0262\u0263\u0001\u0000\u0000\u0000\u0263\u0264\u0006\u0012"+ - "\t\u0000\u02645\u0001\u0000\u0000\u0000\u0265\u0266\u0004\u0013\u0003"+ - "\u0000\u0266\u0267\u0007\u0015\u0000\u0000\u0267\u0268\u0007\u0007\u0000"+ - "\u0000\u0268\u0269\u0007\u0001\u0000\u0000\u0269\u026a\u0007\t\u0000\u0000"+ - "\u026a\u026b\u0001\u0000\u0000\u0000\u026b\u026c\u0006\u0013\n\u0000\u026c"+ - "7\u0001\u0000\u0000\u0000\u026d\u026e\u0004\u0014\u0004\u0000\u026e\u026f"+ - "\u0007\u000f\u0000\u0000\u026f\u0270\u0007\u0014\u0000\u0000\u0270\u0271"+ - "\u0007\r\u0000\u0000\u0271\u0272\u0007\r\u0000\u0000\u0272\u0273\u0001"+ - "\u0000\u0000\u0000\u0273\u0274\u0006\u0014\n\u0000\u02749\u0001\u0000"+ - "\u0000\u0000\u0275\u0276\u0004\u0015\u0005\u0000\u0276\u0277\u0007\r\u0000"+ - "\u0000\u0277\u0278\u0007\u0003\u0000\u0000\u0278\u0279\u0007\u000f\u0000"+ - "\u0000\u0279\u027a\u0007\u0005\u0000\u0000\u027a\u027b\u0001\u0000\u0000"+ - "\u0000\u027b\u027c\u0006\u0015\n\u0000\u027c;\u0001\u0000\u0000\u0000"+ - "\u027d\u027e\u0004\u0016\u0006\u0000\u027e\u027f\u0007\u0006\u0000\u0000"+ - "\u027f\u0280\u0007\u0001\u0000\u0000\u0280\u0281\u0007\u0011\u0000\u0000"+ - "\u0281\u0282\u0007\n\u0000\u0000\u0282\u0283\u0007\u0005\u0000\u0000\u0283"+ - "\u0284\u0001\u0000\u0000\u0000\u0284\u0285\u0006\u0016\n\u0000\u0285="+ - "\u0001\u0000\u0000\u0000\u0286\u0287\u0004\u0017\u0007\u0000\u0287\u0288"+ - "\u0007\r\u0000\u0000\u0288\u0289\u0007\u0007\u0000\u0000\u0289\u028a\u0007"+ - "\u0007\u0000\u0000\u028a\u028b\u0007\u0012\u0000\u0000\u028b\u028c\u0007"+ - "\u0014\u0000\u0000\u028c\u028d\u0007\b\u0000\u0000\u028d\u028e\u0001\u0000"+ - "\u0000\u0000\u028e\u028f\u0006\u0017\n\u0000\u028f?\u0001\u0000\u0000"+ - "\u0000\u0290\u0292\b\u0016\u0000\u0000\u0291\u0290\u0001\u0000\u0000\u0000"+ - "\u0292\u0293\u0001\u0000\u0000\u0000\u0293\u0291\u0001\u0000\u0000\u0000"+ - "\u0293\u0294\u0001\u0000\u0000\u0000\u0294\u0295\u0001\u0000\u0000\u0000"+ - "\u0295\u0296\u0006\u0018\u0000\u0000\u0296A\u0001\u0000\u0000\u0000\u0297"+ - "\u0298\u0005/\u0000\u0000\u0298\u0299\u0005/\u0000\u0000\u0299\u029d\u0001"+ - "\u0000\u0000\u0000\u029a\u029c\b\u0017\u0000\u0000\u029b\u029a\u0001\u0000"+ - "\u0000\u0000\u029c\u029f\u0001\u0000\u0000\u0000\u029d\u029b\u0001\u0000"+ - "\u0000\u0000\u029d\u029e\u0001\u0000\u0000\u0000\u029e\u02a1\u0001\u0000"+ - "\u0000\u0000\u029f\u029d\u0001\u0000\u0000\u0000\u02a0\u02a2\u0005\r\u0000"+ - "\u0000\u02a1\u02a0\u0001\u0000\u0000\u0000\u02a1\u02a2\u0001\u0000\u0000"+ - "\u0000\u02a2\u02a4\u0001\u0000\u0000\u0000\u02a3\u02a5\u0005\n\u0000\u0000"+ - "\u02a4\u02a3\u0001\u0000\u0000\u0000\u02a4\u02a5\u0001\u0000\u0000\u0000"+ - "\u02a5\u02a6\u0001\u0000\u0000\u0000\u02a6\u02a7\u0006\u0019\u000b\u0000"+ - "\u02a7C\u0001\u0000\u0000\u0000\u02a8\u02a9\u0005/\u0000\u0000\u02a9\u02aa"+ - "\u0005*\u0000\u0000\u02aa\u02af\u0001\u0000\u0000\u0000\u02ab\u02ae\u0003"+ - "D\u001a\u0000\u02ac\u02ae\t\u0000\u0000\u0000\u02ad\u02ab\u0001\u0000"+ - "\u0000\u0000\u02ad\u02ac\u0001\u0000\u0000\u0000\u02ae\u02b1\u0001\u0000"+ - "\u0000\u0000\u02af\u02b0\u0001\u0000\u0000\u0000\u02af\u02ad\u0001\u0000"+ - "\u0000\u0000\u02b0\u02b2\u0001\u0000\u0000\u0000\u02b1\u02af\u0001\u0000"+ - "\u0000\u0000\u02b2\u02b3\u0005*\u0000\u0000\u02b3\u02b4\u0005/\u0000\u0000"+ - "\u02b4\u02b5\u0001\u0000\u0000\u0000\u02b5\u02b6\u0006\u001a\u000b\u0000"+ - "\u02b6E\u0001\u0000\u0000\u0000\u02b7\u02b9\u0007\u0018\u0000\u0000\u02b8"+ - "\u02b7\u0001\u0000\u0000\u0000\u02b9\u02ba\u0001\u0000\u0000\u0000\u02ba"+ - "\u02b8\u0001\u0000\u0000\u0000\u02ba\u02bb\u0001\u0000\u0000\u0000\u02bb"+ - "\u02bc\u0001\u0000\u0000\u0000\u02bc\u02bd\u0006\u001b\u000b\u0000\u02bd"+ - "G\u0001\u0000\u0000\u0000\u02be\u02bf\u0005|\u0000\u0000\u02bf\u02c0\u0001"+ - "\u0000\u0000\u0000\u02c0\u02c1\u0006\u001c\f\u0000\u02c1I\u0001\u0000"+ - "\u0000\u0000\u02c2\u02c3\u0007\u0019\u0000\u0000\u02c3K\u0001\u0000\u0000"+ - "\u0000\u02c4\u02c5\u0007\u001a\u0000\u0000\u02c5M\u0001\u0000\u0000\u0000"+ - "\u02c6\u02c7\u0005\\\u0000\u0000\u02c7\u02c8\u0007\u001b\u0000\u0000\u02c8"+ - "O\u0001\u0000\u0000\u0000\u02c9\u02ca\b\u001c\u0000\u0000\u02caQ\u0001"+ - "\u0000\u0000\u0000\u02cb\u02cd\u0007\u0003\u0000\u0000\u02cc\u02ce\u0007"+ - "\u001d\u0000\u0000\u02cd\u02cc\u0001\u0000\u0000\u0000\u02cd\u02ce\u0001"+ - "\u0000\u0000\u0000\u02ce\u02d0\u0001\u0000\u0000\u0000\u02cf\u02d1\u0003"+ - "J\u001d\u0000\u02d0\u02cf\u0001\u0000\u0000\u0000\u02d1\u02d2\u0001\u0000"+ - "\u0000\u0000\u02d2\u02d0\u0001\u0000\u0000\u0000\u02d2\u02d3\u0001\u0000"+ - "\u0000\u0000\u02d3S\u0001\u0000\u0000\u0000\u02d4\u02d5\u0005@\u0000\u0000"+ - "\u02d5U\u0001\u0000\u0000\u0000\u02d6\u02d7\u0005`\u0000\u0000\u02d7W"+ - "\u0001\u0000\u0000\u0000\u02d8\u02dc\b\u001e\u0000\u0000\u02d9\u02da\u0005"+ - "`\u0000\u0000\u02da\u02dc\u0005`\u0000\u0000\u02db\u02d8\u0001\u0000\u0000"+ - "\u0000\u02db\u02d9\u0001\u0000\u0000\u0000\u02dcY\u0001\u0000\u0000\u0000"+ - "\u02dd\u02de\u0005_\u0000\u0000\u02de[\u0001\u0000\u0000\u0000\u02df\u02e3"+ - "\u0003L\u001e\u0000\u02e0\u02e3\u0003J\u001d\u0000\u02e1\u02e3\u0003Z"+ - "%\u0000\u02e2\u02df\u0001\u0000\u0000\u0000\u02e2\u02e0\u0001\u0000\u0000"+ - "\u0000\u02e2\u02e1\u0001\u0000\u0000\u0000\u02e3]\u0001\u0000\u0000\u0000"+ - "\u02e4\u02e9\u0005\"\u0000\u0000\u02e5\u02e8\u0003N\u001f\u0000\u02e6"+ - "\u02e8\u0003P \u0000\u02e7\u02e5\u0001\u0000\u0000\u0000\u02e7\u02e6\u0001"+ - "\u0000\u0000\u0000\u02e8\u02eb\u0001\u0000\u0000\u0000\u02e9\u02e7\u0001"+ - "\u0000\u0000\u0000\u02e9\u02ea\u0001\u0000\u0000\u0000\u02ea\u02ec\u0001"+ - "\u0000\u0000\u0000\u02eb\u02e9\u0001\u0000\u0000\u0000\u02ec\u0302\u0005"+ - "\"\u0000\u0000\u02ed\u02ee\u0005\"\u0000\u0000\u02ee\u02ef\u0005\"\u0000"+ - "\u0000\u02ef\u02f0\u0005\"\u0000\u0000\u02f0\u02f4\u0001\u0000\u0000\u0000"+ - "\u02f1\u02f3\b\u0017\u0000\u0000\u02f2\u02f1\u0001\u0000\u0000\u0000\u02f3"+ - "\u02f6\u0001\u0000\u0000\u0000\u02f4\u02f5\u0001\u0000\u0000\u0000\u02f4"+ - "\u02f2\u0001\u0000\u0000\u0000\u02f5\u02f7\u0001\u0000\u0000\u0000\u02f6"+ - "\u02f4\u0001\u0000\u0000\u0000\u02f7\u02f8\u0005\"\u0000\u0000\u02f8\u02f9"+ - "\u0005\"\u0000\u0000\u02f9\u02fa\u0005\"\u0000\u0000\u02fa\u02fc\u0001"+ - "\u0000\u0000\u0000\u02fb\u02fd\u0005\"\u0000\u0000\u02fc\u02fb\u0001\u0000"+ - "\u0000\u0000\u02fc\u02fd\u0001\u0000\u0000\u0000\u02fd\u02ff\u0001\u0000"+ - "\u0000\u0000\u02fe\u0300\u0005\"\u0000\u0000\u02ff\u02fe\u0001\u0000\u0000"+ - "\u0000\u02ff\u0300\u0001\u0000\u0000\u0000\u0300\u0302\u0001\u0000\u0000"+ - "\u0000\u0301\u02e4\u0001\u0000\u0000\u0000\u0301\u02ed\u0001\u0000\u0000"+ - "\u0000\u0302_\u0001\u0000\u0000\u0000\u0303\u0305\u0003J\u001d\u0000\u0304"+ - "\u0303\u0001\u0000\u0000\u0000\u0305\u0306\u0001\u0000\u0000\u0000\u0306"+ - "\u0304\u0001\u0000\u0000\u0000\u0306\u0307\u0001\u0000\u0000\u0000\u0307"+ - "a\u0001\u0000\u0000\u0000\u0308\u030a\u0003J\u001d\u0000\u0309\u0308\u0001"+ - "\u0000\u0000\u0000\u030a\u030b\u0001\u0000\u0000\u0000\u030b\u0309\u0001"+ - "\u0000\u0000\u0000\u030b\u030c\u0001\u0000\u0000\u0000\u030c\u030d\u0001"+ - "\u0000\u0000\u0000\u030d\u0311\u0003t2\u0000\u030e\u0310\u0003J\u001d"+ - "\u0000\u030f\u030e\u0001\u0000\u0000\u0000\u0310\u0313\u0001\u0000\u0000"+ - "\u0000\u0311\u030f\u0001\u0000\u0000\u0000\u0311\u0312\u0001\u0000\u0000"+ - "\u0000\u0312\u0333\u0001\u0000\u0000\u0000\u0313\u0311\u0001\u0000\u0000"+ - "\u0000\u0314\u0316\u0003t2\u0000\u0315\u0317\u0003J\u001d\u0000\u0316"+ - "\u0315\u0001\u0000\u0000\u0000\u0317\u0318\u0001\u0000\u0000\u0000\u0318"+ - "\u0316\u0001\u0000\u0000\u0000\u0318\u0319\u0001\u0000\u0000\u0000\u0319"+ - "\u0333\u0001\u0000\u0000\u0000\u031a\u031c\u0003J\u001d\u0000\u031b\u031a"+ - "\u0001\u0000\u0000\u0000\u031c\u031d\u0001\u0000\u0000\u0000\u031d\u031b"+ - "\u0001\u0000\u0000\u0000\u031d\u031e\u0001\u0000\u0000\u0000\u031e\u0326"+ - "\u0001\u0000\u0000\u0000\u031f\u0323\u0003t2\u0000\u0320\u0322\u0003J"+ - "\u001d\u0000\u0321\u0320\u0001\u0000\u0000\u0000\u0322\u0325\u0001\u0000"+ - "\u0000\u0000\u0323\u0321\u0001\u0000\u0000\u0000\u0323\u0324\u0001\u0000"+ - "\u0000\u0000\u0324\u0327\u0001\u0000\u0000\u0000\u0325\u0323\u0001\u0000"+ - "\u0000\u0000\u0326\u031f\u0001\u0000\u0000\u0000\u0326\u0327\u0001\u0000"+ - "\u0000\u0000\u0327\u0328\u0001\u0000\u0000\u0000\u0328\u0329\u0003R!\u0000"+ - "\u0329\u0333\u0001\u0000\u0000\u0000\u032a\u032c\u0003t2\u0000\u032b\u032d"+ - "\u0003J\u001d\u0000\u032c\u032b\u0001\u0000\u0000\u0000\u032d\u032e\u0001"+ - "\u0000\u0000\u0000\u032e\u032c\u0001\u0000\u0000\u0000\u032e\u032f\u0001"+ - "\u0000\u0000\u0000\u032f\u0330\u0001\u0000\u0000\u0000\u0330\u0331\u0003"+ - "R!\u0000\u0331\u0333\u0001\u0000\u0000\u0000\u0332\u0309\u0001\u0000\u0000"+ - "\u0000\u0332\u0314\u0001\u0000\u0000\u0000\u0332\u031b\u0001\u0000\u0000"+ - "\u0000\u0332\u032a\u0001\u0000\u0000\u0000\u0333c\u0001\u0000\u0000\u0000"+ - "\u0334\u0335\u0007\u001f\u0000\u0000\u0335\u0336\u0007 \u0000\u0000\u0336"+ - "e\u0001\u0000\u0000\u0000\u0337\u0338\u0007\f\u0000\u0000\u0338\u0339"+ - "\u0007\t\u0000\u0000\u0339\u033a\u0007\u0000\u0000\u0000\u033ag\u0001"+ - "\u0000\u0000\u0000\u033b\u033c\u0007\f\u0000\u0000\u033c\u033d\u0007\u0002"+ - "\u0000\u0000\u033d\u033e\u0007\u0004\u0000\u0000\u033ei\u0001\u0000\u0000"+ - "\u0000\u033f\u0340\u0005=\u0000\u0000\u0340k\u0001\u0000\u0000\u0000\u0341"+ - "\u0342\u0005:\u0000\u0000\u0342\u0343\u0005:\u0000\u0000\u0343m\u0001"+ - "\u0000\u0000\u0000\u0344\u0345\u0005:\u0000\u0000\u0345o\u0001\u0000\u0000"+ - "\u0000\u0346\u0347\u0005,\u0000\u0000\u0347q\u0001\u0000\u0000\u0000\u0348"+ - "\u0349\u0007\u0000\u0000\u0000\u0349\u034a\u0007\u0003\u0000\u0000\u034a"+ - "\u034b\u0007\u0002\u0000\u0000\u034b\u034c\u0007\u0004\u0000\u0000\u034c"+ - "s\u0001\u0000\u0000\u0000\u034d\u034e\u0005.\u0000\u0000\u034eu\u0001"+ - "\u0000\u0000\u0000\u034f\u0350\u0007\u000f\u0000\u0000\u0350\u0351\u0007"+ - "\f\u0000\u0000\u0351\u0352\u0007\r\u0000\u0000\u0352\u0353\u0007\u0002"+ - "\u0000\u0000\u0353\u0354\u0007\u0003\u0000\u0000\u0354w\u0001\u0000\u0000"+ - "\u0000\u0355\u0356\u0007\u000f\u0000\u0000\u0356\u0357\u0007\u0001\u0000"+ - "\u0000\u0357\u0358\u0007\u0006\u0000\u0000\u0358\u0359\u0007\u0002\u0000"+ - "\u0000\u0359\u035a\u0007\u0005\u0000\u0000\u035ay\u0001\u0000\u0000\u0000"+ - "\u035b\u035c\u0007\u0001\u0000\u0000\u035c\u035d\u0007\t\u0000\u0000\u035d"+ - "{\u0001\u0000\u0000\u0000\u035e\u035f\u0007\u0001\u0000\u0000\u035f\u0360"+ - "\u0007\u0002\u0000\u0000\u0360}\u0001\u0000\u0000\u0000\u0361\u0362\u0007"+ - "\r\u0000\u0000\u0362\u0363\u0007\f\u0000\u0000\u0363\u0364\u0007\u0002"+ - "\u0000\u0000\u0364\u0365\u0007\u0005\u0000\u0000\u0365\u007f\u0001\u0000"+ - "\u0000\u0000\u0366\u0367\u0007\r\u0000\u0000\u0367\u0368\u0007\u0001\u0000"+ - "\u0000\u0368\u0369\u0007\u0012\u0000\u0000\u0369\u036a\u0007\u0003\u0000"+ - "\u0000\u036a\u0081\u0001\u0000\u0000\u0000\u036b\u036c\u0005(\u0000\u0000"+ - "\u036c\u0083\u0001\u0000\u0000\u0000\u036d\u036e\u0007\t\u0000\u0000\u036e"+ - "\u036f\u0007\u0007\u0000\u0000\u036f\u0370\u0007\u0005\u0000\u0000\u0370"+ - "\u0085\u0001\u0000\u0000\u0000\u0371\u0372\u0007\t\u0000\u0000\u0372\u0373"+ - "\u0007\u0014\u0000\u0000\u0373\u0374\u0007\r\u0000\u0000\u0374\u0375\u0007"+ - "\r\u0000\u0000\u0375\u0087\u0001\u0000\u0000\u0000\u0376\u0377\u0007\t"+ - "\u0000\u0000\u0377\u0378\u0007\u0014\u0000\u0000\u0378\u0379\u0007\r\u0000"+ - "\u0000\u0379\u037a\u0007\r\u0000\u0000\u037a\u037b\u0007\u0002\u0000\u0000"+ - "\u037b\u0089\u0001\u0000\u0000\u0000\u037c\u037d\u0007\u0007\u0000\u0000"+ - "\u037d\u037e\u0007\u0006\u0000\u0000\u037e\u008b\u0001\u0000\u0000\u0000"+ - "\u037f\u0380\u0005?\u0000\u0000\u0380\u008d\u0001\u0000\u0000\u0000\u0381"+ - "\u0382\u0007\u0006\u0000\u0000\u0382\u0383\u0007\r\u0000\u0000\u0383\u0384"+ - "\u0007\u0001\u0000\u0000\u0384\u0385\u0007\u0012\u0000\u0000\u0385\u0386"+ - "\u0007\u0003\u0000\u0000\u0386\u008f\u0001\u0000\u0000\u0000\u0387\u0388"+ - "\u0005)\u0000\u0000\u0388\u0091\u0001\u0000\u0000\u0000\u0389\u038a\u0007"+ - "\u0005\u0000\u0000\u038a\u038b\u0007\u0006\u0000\u0000\u038b\u038c\u0007"+ - "\u0014\u0000\u0000\u038c\u038d\u0007\u0003\u0000\u0000\u038d\u0093\u0001"+ - "\u0000\u0000\u0000\u038e\u038f\u0005=\u0000\u0000\u038f\u0390\u0005=\u0000"+ - "\u0000\u0390\u0095\u0001\u0000\u0000\u0000\u0391\u0392\u0005=\u0000\u0000"+ - "\u0392\u0393\u0005~\u0000\u0000\u0393\u0097\u0001\u0000\u0000\u0000\u0394"+ - "\u0395\u0005!\u0000\u0000\u0395\u0396\u0005=\u0000\u0000\u0396\u0099\u0001"+ - "\u0000\u0000\u0000\u0397\u0398\u0005<\u0000\u0000\u0398\u009b\u0001\u0000"+ - "\u0000\u0000\u0399\u039a\u0005<\u0000\u0000\u039a\u039b\u0005=\u0000\u0000"+ - "\u039b\u009d\u0001\u0000\u0000\u0000\u039c\u039d\u0005>\u0000\u0000\u039d"+ - "\u009f\u0001\u0000\u0000\u0000\u039e\u039f\u0005>\u0000\u0000\u039f\u03a0"+ - "\u0005=\u0000\u0000\u03a0\u00a1\u0001\u0000\u0000\u0000\u03a1\u03a2\u0005"+ - "+\u0000\u0000\u03a2\u00a3\u0001\u0000\u0000\u0000\u03a3\u03a4\u0005-\u0000"+ - "\u0000\u03a4\u00a5\u0001\u0000\u0000\u0000\u03a5\u03a6\u0005*\u0000\u0000"+ - "\u03a6\u00a7\u0001\u0000\u0000\u0000\u03a7\u03a8\u0005/\u0000\u0000\u03a8"+ - "\u00a9\u0001\u0000\u0000\u0000\u03a9\u03aa\u0005%\u0000\u0000\u03aa\u00ab"+ - "\u0001\u0000\u0000\u0000\u03ab\u03ac\u0004N\b\u0000\u03ac\u03ad\u0005"+ - "{\u0000\u0000\u03ad\u00ad\u0001\u0000\u0000\u0000\u03ae\u03af\u0004O\t"+ - "\u0000\u03af\u03b0\u0005}\u0000\u0000\u03b0\u00af\u0001\u0000\u0000\u0000"+ - "\u03b1\u03b2\u0003.\u000f\u0000\u03b2\u03b3\u0001\u0000\u0000\u0000\u03b3"+ - "\u03b4\u0006P\r\u0000\u03b4\u00b1\u0001\u0000\u0000\u0000\u03b5\u03b8"+ - "\u0003\u008c>\u0000\u03b6\u03b9\u0003L\u001e\u0000\u03b7\u03b9\u0003Z"+ - "%\u0000\u03b8\u03b6\u0001\u0000\u0000\u0000\u03b8\u03b7\u0001\u0000\u0000"+ - "\u0000\u03b9\u03bd\u0001\u0000\u0000\u0000\u03ba\u03bc\u0003\\&\u0000"+ - "\u03bb\u03ba\u0001\u0000\u0000\u0000\u03bc\u03bf\u0001\u0000\u0000\u0000"+ - "\u03bd\u03bb\u0001\u0000\u0000\u0000\u03bd\u03be\u0001\u0000\u0000\u0000"+ - "\u03be\u03c7\u0001\u0000\u0000\u0000\u03bf\u03bd\u0001\u0000\u0000\u0000"+ - "\u03c0\u03c2\u0003\u008c>\u0000\u03c1\u03c3\u0003J\u001d\u0000\u03c2\u03c1"+ - "\u0001\u0000\u0000\u0000\u03c3\u03c4\u0001\u0000\u0000\u0000\u03c4\u03c2"+ - "\u0001\u0000\u0000\u0000\u03c4\u03c5\u0001\u0000\u0000\u0000\u03c5\u03c7"+ - "\u0001\u0000\u0000\u0000\u03c6\u03b5\u0001\u0000\u0000\u0000\u03c6\u03c0"+ - "\u0001\u0000\u0000\u0000\u03c7\u00b3\u0001\u0000\u0000\u0000\u03c8\u03c9"+ - "\u0005[\u0000\u0000\u03c9\u03ca\u0001\u0000\u0000\u0000\u03ca\u03cb\u0006"+ - "R\u0000\u0000\u03cb\u03cc\u0006R\u0000\u0000\u03cc\u00b5\u0001\u0000\u0000"+ - "\u0000\u03cd\u03ce\u0005]\u0000\u0000\u03ce\u03cf\u0001\u0000\u0000\u0000"+ - "\u03cf\u03d0\u0006S\f\u0000\u03d0\u03d1\u0006S\f\u0000\u03d1\u00b7\u0001"+ - "\u0000\u0000\u0000\u03d2\u03d6\u0003L\u001e\u0000\u03d3\u03d5\u0003\\"+ - "&\u0000\u03d4\u03d3\u0001\u0000\u0000\u0000\u03d5\u03d8\u0001\u0000\u0000"+ - "\u0000\u03d6\u03d4\u0001\u0000\u0000\u0000\u03d6\u03d7\u0001\u0000\u0000"+ - "\u0000\u03d7\u03e3\u0001\u0000\u0000\u0000\u03d8\u03d6\u0001\u0000\u0000"+ - "\u0000\u03d9\u03dc\u0003Z%\u0000\u03da\u03dc\u0003T\"\u0000\u03db\u03d9"+ - "\u0001\u0000\u0000\u0000\u03db\u03da\u0001\u0000\u0000\u0000\u03dc\u03de"+ - "\u0001\u0000\u0000\u0000\u03dd\u03df\u0003\\&\u0000\u03de\u03dd\u0001"+ - "\u0000\u0000\u0000\u03df\u03e0\u0001\u0000\u0000\u0000\u03e0\u03de\u0001"+ - "\u0000\u0000\u0000\u03e0\u03e1\u0001\u0000\u0000\u0000\u03e1\u03e3\u0001"+ - "\u0000\u0000\u0000\u03e2\u03d2\u0001\u0000\u0000\u0000\u03e2\u03db\u0001"+ - "\u0000\u0000\u0000\u03e3\u00b9\u0001\u0000\u0000\u0000\u03e4\u03e6\u0003"+ - "V#\u0000\u03e5\u03e7\u0003X$\u0000\u03e6\u03e5\u0001\u0000\u0000\u0000"+ - "\u03e7\u03e8\u0001\u0000\u0000\u0000\u03e8\u03e6\u0001\u0000\u0000\u0000"+ - "\u03e8\u03e9\u0001\u0000\u0000\u0000\u03e9\u03ea\u0001\u0000\u0000\u0000"+ - "\u03ea\u03eb\u0003V#\u0000\u03eb\u00bb\u0001\u0000\u0000\u0000\u03ec\u03ed"+ - "\u0003\u00baU\u0000\u03ed\u00bd\u0001\u0000\u0000\u0000\u03ee\u03ef\u0003"+ - "B\u0019\u0000\u03ef\u03f0\u0001\u0000\u0000\u0000\u03f0\u03f1\u0006W\u000b"+ - "\u0000\u03f1\u00bf\u0001\u0000\u0000\u0000\u03f2\u03f3\u0003D\u001a\u0000"+ - "\u03f3\u03f4\u0001\u0000\u0000\u0000\u03f4\u03f5\u0006X\u000b\u0000\u03f5"+ - "\u00c1\u0001\u0000\u0000\u0000\u03f6\u03f7\u0003F\u001b\u0000\u03f7\u03f8"+ - "\u0001\u0000\u0000\u0000\u03f8\u03f9\u0006Y\u000b\u0000\u03f9\u00c3\u0001"+ - "\u0000\u0000\u0000\u03fa\u03fb\u0003\u00b4R\u0000\u03fb\u03fc\u0001\u0000"+ - "\u0000\u0000\u03fc\u03fd\u0006Z\u000e\u0000\u03fd\u03fe\u0006Z\u000f\u0000"+ - "\u03fe\u00c5\u0001\u0000\u0000\u0000\u03ff\u0400\u0003H\u001c\u0000\u0400"+ - "\u0401\u0001\u0000\u0000\u0000\u0401\u0402\u0006[\u0010\u0000\u0402\u0403"+ - "\u0006[\f\u0000\u0403\u00c7\u0001\u0000\u0000\u0000\u0404\u0405\u0003"+ - "F\u001b\u0000\u0405\u0406\u0001\u0000\u0000\u0000\u0406\u0407\u0006\\"+ - "\u000b\u0000\u0407\u00c9\u0001\u0000\u0000\u0000\u0408\u0409\u0003B\u0019"+ - "\u0000\u0409\u040a\u0001\u0000\u0000\u0000\u040a\u040b\u0006]\u000b\u0000"+ - "\u040b\u00cb\u0001\u0000\u0000\u0000\u040c\u040d\u0003D\u001a\u0000\u040d"+ - "\u040e\u0001\u0000\u0000\u0000\u040e\u040f\u0006^\u000b\u0000\u040f\u00cd"+ - "\u0001\u0000\u0000\u0000\u0410\u0411\u0003H\u001c\u0000\u0411\u0412\u0001"+ - "\u0000\u0000\u0000\u0412\u0413\u0006_\u0010\u0000\u0413\u0414\u0006_\f"+ - "\u0000\u0414\u00cf\u0001\u0000\u0000\u0000\u0415\u0416\u0003\u00b4R\u0000"+ - "\u0416\u0417\u0001\u0000\u0000\u0000\u0417\u0418\u0006`\u000e\u0000\u0418"+ - "\u00d1\u0001\u0000\u0000\u0000\u0419\u041a\u0003\u00b6S\u0000\u041a\u041b"+ - "\u0001\u0000\u0000\u0000\u041b\u041c\u0006a\u0011\u0000\u041c\u00d3\u0001"+ - "\u0000\u0000\u0000\u041d\u041e\u0003n/\u0000\u041e\u041f\u0001\u0000\u0000"+ - "\u0000\u041f\u0420\u0006b\u0012\u0000\u0420\u00d5\u0001\u0000\u0000\u0000"+ - "\u0421\u0422\u0003p0\u0000\u0422\u0423\u0001\u0000\u0000\u0000\u0423\u0424"+ - "\u0006c\u0013\u0000\u0424\u00d7\u0001\u0000\u0000\u0000\u0425\u0426\u0003"+ - "j-\u0000\u0426\u0427\u0001\u0000\u0000\u0000\u0427\u0428\u0006d\u0014"+ - "\u0000\u0428\u00d9\u0001\u0000\u0000\u0000\u0429\u042a\u0007\u0010\u0000"+ - "\u0000\u042a\u042b\u0007\u0003\u0000\u0000\u042b\u042c\u0007\u0005\u0000"+ - "\u0000\u042c\u042d\u0007\f\u0000\u0000\u042d\u042e\u0007\u0000\u0000\u0000"+ - "\u042e\u042f\u0007\f\u0000\u0000\u042f\u0430\u0007\u0005\u0000\u0000\u0430"+ - "\u0431\u0007\f\u0000\u0000\u0431\u00db\u0001\u0000\u0000\u0000\u0432\u0436"+ - "\b!\u0000\u0000\u0433\u0434\u0005/\u0000\u0000\u0434\u0436\b\"\u0000\u0000"+ - "\u0435\u0432\u0001\u0000\u0000\u0000\u0435\u0433\u0001\u0000\u0000\u0000"+ - "\u0436\u00dd\u0001\u0000\u0000\u0000\u0437\u0439\u0003\u00dcf\u0000\u0438"+ - "\u0437\u0001\u0000\u0000\u0000\u0439\u043a\u0001\u0000\u0000\u0000\u043a"+ - "\u0438\u0001\u0000\u0000\u0000\u043a\u043b\u0001\u0000\u0000\u0000\u043b"+ - "\u00df\u0001\u0000\u0000\u0000\u043c\u043d\u0003\u00deg\u0000\u043d\u043e"+ - "\u0001\u0000\u0000\u0000\u043e\u043f\u0006h\u0015\u0000\u043f\u00e1\u0001"+ - "\u0000\u0000\u0000\u0440\u0441\u0003^\'\u0000\u0441\u0442\u0001\u0000"+ - "\u0000\u0000\u0442\u0443\u0006i\u0016\u0000\u0443\u00e3\u0001\u0000\u0000"+ - "\u0000\u0444\u0445\u0003B\u0019\u0000\u0445\u0446\u0001\u0000\u0000\u0000"+ - "\u0446\u0447\u0006j\u000b\u0000\u0447\u00e5\u0001\u0000\u0000\u0000\u0448"+ - "\u0449\u0003D\u001a\u0000\u0449\u044a\u0001\u0000\u0000\u0000\u044a\u044b"+ - "\u0006k\u000b\u0000\u044b\u00e7\u0001\u0000\u0000\u0000\u044c\u044d\u0003"+ - "F\u001b\u0000\u044d\u044e\u0001\u0000\u0000\u0000\u044e\u044f\u0006l\u000b"+ - "\u0000\u044f\u00e9\u0001\u0000\u0000\u0000\u0450\u0451\u0003H\u001c\u0000"+ - "\u0451\u0452\u0001\u0000\u0000\u0000\u0452\u0453\u0006m\u0010\u0000\u0453"+ - "\u0454\u0006m\f\u0000\u0454\u00eb\u0001\u0000\u0000\u0000\u0455\u0456"+ - "\u0003t2\u0000\u0456\u0457\u0001\u0000\u0000\u0000\u0457\u0458\u0006n"+ - "\u0017\u0000\u0458\u00ed\u0001\u0000\u0000\u0000\u0459\u045a\u0003p0\u0000"+ - "\u045a\u045b\u0001\u0000\u0000\u0000\u045b\u045c\u0006o\u0013\u0000\u045c"+ - "\u00ef\u0001\u0000\u0000\u0000\u045d\u045e\u0004p\n\u0000\u045e\u045f"+ - "\u0003\u008c>\u0000\u045f\u0460\u0001\u0000\u0000\u0000\u0460\u0461\u0006"+ - "p\u0018\u0000\u0461\u00f1\u0001\u0000\u0000\u0000\u0462\u0463\u0004q\u000b"+ - "\u0000\u0463\u0464\u0003\u00b2Q\u0000\u0464\u0465\u0001\u0000\u0000\u0000"+ - "\u0465\u0466\u0006q\u0019\u0000\u0466\u00f3\u0001\u0000\u0000\u0000\u0467"+ - "\u046c\u0003L\u001e\u0000\u0468\u046c\u0003J\u001d\u0000\u0469\u046c\u0003"+ - "Z%\u0000\u046a\u046c\u0003\u00a6K\u0000\u046b\u0467\u0001\u0000\u0000"+ - "\u0000\u046b\u0468\u0001\u0000\u0000\u0000\u046b\u0469\u0001\u0000\u0000"+ - "\u0000\u046b\u046a\u0001\u0000\u0000\u0000\u046c\u00f5\u0001\u0000\u0000"+ - "\u0000\u046d\u0470\u0003L\u001e\u0000\u046e\u0470\u0003\u00a6K\u0000\u046f"+ - "\u046d\u0001\u0000\u0000\u0000\u046f\u046e\u0001\u0000\u0000\u0000\u0470"+ - "\u0474\u0001\u0000\u0000\u0000\u0471\u0473\u0003\u00f4r\u0000\u0472\u0471"+ - "\u0001\u0000\u0000\u0000\u0473\u0476\u0001\u0000\u0000\u0000\u0474\u0472"+ - "\u0001\u0000\u0000\u0000\u0474\u0475\u0001\u0000\u0000\u0000\u0475\u0481"+ - "\u0001\u0000\u0000\u0000\u0476\u0474\u0001\u0000\u0000\u0000\u0477\u047a"+ - "\u0003Z%\u0000\u0478\u047a\u0003T\"\u0000\u0479\u0477\u0001\u0000\u0000"+ - "\u0000\u0479\u0478\u0001\u0000\u0000\u0000\u047a\u047c\u0001\u0000\u0000"+ - "\u0000\u047b\u047d\u0003\u00f4r\u0000\u047c\u047b\u0001\u0000\u0000\u0000"+ - "\u047d\u047e\u0001\u0000\u0000\u0000\u047e\u047c\u0001\u0000\u0000\u0000"+ - "\u047e\u047f\u0001\u0000\u0000\u0000\u047f\u0481\u0001\u0000\u0000\u0000"+ - "\u0480\u046f\u0001\u0000\u0000\u0000\u0480\u0479\u0001\u0000\u0000\u0000"+ - "\u0481\u00f7\u0001\u0000\u0000\u0000\u0482\u0485\u0003\u00f6s\u0000\u0483"+ - "\u0485\u0003\u00baU\u0000\u0484\u0482\u0001\u0000\u0000\u0000\u0484\u0483"+ - "\u0001\u0000\u0000\u0000\u0485\u0486\u0001\u0000\u0000\u0000\u0486\u0484"+ - "\u0001\u0000\u0000\u0000\u0486\u0487\u0001\u0000\u0000\u0000\u0487\u00f9"+ - "\u0001\u0000\u0000\u0000\u0488\u0489\u0003B\u0019\u0000\u0489\u048a\u0001"+ - "\u0000\u0000\u0000\u048a\u048b\u0006u\u000b\u0000\u048b\u00fb\u0001\u0000"+ - "\u0000\u0000\u048c\u048d\u0003D\u001a\u0000\u048d\u048e\u0001\u0000\u0000"+ - "\u0000\u048e\u048f\u0006v\u000b\u0000\u048f\u00fd\u0001\u0000\u0000\u0000"+ - "\u0490\u0491\u0003F\u001b\u0000\u0491\u0492\u0001\u0000\u0000\u0000\u0492"+ - "\u0493\u0006w\u000b\u0000\u0493\u00ff\u0001\u0000\u0000\u0000\u0494\u0495"+ - "\u0003H\u001c\u0000\u0495\u0496\u0001\u0000\u0000\u0000\u0496\u0497\u0006"+ - "x\u0010\u0000\u0497\u0498\u0006x\f\u0000\u0498\u0101\u0001\u0000\u0000"+ - "\u0000\u0499\u049a\u0003j-\u0000\u049a\u049b\u0001\u0000\u0000\u0000\u049b"+ - "\u049c\u0006y\u0014\u0000\u049c\u0103\u0001\u0000\u0000\u0000\u049d\u049e"+ - "\u0003p0\u0000\u049e\u049f\u0001\u0000\u0000\u0000\u049f\u04a0\u0006z"+ - "\u0013\u0000\u04a0\u0105\u0001\u0000\u0000\u0000\u04a1\u04a2\u0003t2\u0000"+ - "\u04a2\u04a3\u0001\u0000\u0000\u0000\u04a3\u04a4\u0006{\u0017\u0000\u04a4"+ - "\u0107\u0001\u0000\u0000\u0000\u04a5\u04a6\u0004|\f\u0000\u04a6\u04a7"+ - "\u0003\u008c>\u0000\u04a7\u04a8\u0001\u0000\u0000\u0000\u04a8\u04a9\u0006"+ - "|\u0018\u0000\u04a9\u0109\u0001\u0000\u0000\u0000\u04aa\u04ab\u0004}\r"+ - "\u0000\u04ab\u04ac\u0003\u00b2Q\u0000\u04ac\u04ad\u0001\u0000\u0000\u0000"+ - "\u04ad\u04ae\u0006}\u0019\u0000\u04ae\u010b\u0001\u0000\u0000\u0000\u04af"+ - "\u04b0\u0007\f\u0000\u0000\u04b0\u04b1\u0007\u0002\u0000\u0000\u04b1\u010d"+ - "\u0001\u0000\u0000\u0000\u04b2\u04b3\u0003\u00f8t\u0000\u04b3\u04b4\u0001"+ - "\u0000\u0000\u0000\u04b4\u04b5\u0006\u007f\u001a\u0000\u04b5\u010f\u0001"+ - "\u0000\u0000\u0000\u04b6\u04b7\u0003B\u0019\u0000\u04b7\u04b8\u0001\u0000"+ - "\u0000\u0000\u04b8\u04b9\u0006\u0080\u000b\u0000\u04b9\u0111\u0001\u0000"+ - "\u0000\u0000\u04ba\u04bb\u0003D\u001a\u0000\u04bb\u04bc\u0001\u0000\u0000"+ - "\u0000\u04bc\u04bd\u0006\u0081\u000b\u0000\u04bd\u0113\u0001\u0000\u0000"+ - "\u0000\u04be\u04bf\u0003F\u001b\u0000\u04bf\u04c0\u0001\u0000\u0000\u0000"+ - "\u04c0\u04c1\u0006\u0082\u000b\u0000\u04c1\u0115\u0001\u0000\u0000\u0000"+ - "\u04c2\u04c3\u0003H\u001c\u0000\u04c3\u04c4\u0001\u0000\u0000\u0000\u04c4"+ - "\u04c5\u0006\u0083\u0010\u0000\u04c5\u04c6\u0006\u0083\f\u0000\u04c6\u0117"+ - "\u0001\u0000\u0000\u0000\u04c7\u04c8\u0003\u00b4R\u0000\u04c8\u04c9\u0001"+ - "\u0000\u0000\u0000\u04c9\u04ca\u0006\u0084\u000e\u0000\u04ca\u04cb\u0006"+ - "\u0084\u001b\u0000\u04cb\u0119\u0001\u0000\u0000\u0000\u04cc\u04cd\u0007"+ - "\u0007\u0000\u0000\u04cd\u04ce\u0007\t\u0000\u0000\u04ce\u04cf\u0001\u0000"+ - "\u0000\u0000\u04cf\u04d0\u0006\u0085\u001c\u0000\u04d0\u011b\u0001\u0000"+ - "\u0000\u0000\u04d1\u04d2\u0007\u0013\u0000\u0000\u04d2\u04d3\u0007\u0001"+ - "\u0000\u0000\u04d3\u04d4\u0007\u0005\u0000\u0000\u04d4\u04d5\u0007\n\u0000"+ - "\u0000\u04d5\u04d6\u0001\u0000\u0000\u0000\u04d6\u04d7\u0006\u0086\u001c"+ - "\u0000\u04d7\u011d\u0001\u0000\u0000\u0000\u04d8\u04d9\b#\u0000\u0000"+ - "\u04d9\u011f\u0001\u0000\u0000\u0000\u04da\u04dc\u0003\u011e\u0087\u0000"+ - "\u04db\u04da\u0001\u0000\u0000\u0000\u04dc\u04dd\u0001\u0000\u0000\u0000"+ - "\u04dd\u04db\u0001\u0000\u0000\u0000\u04dd\u04de\u0001\u0000\u0000\u0000"+ - "\u04de\u04df\u0001\u0000\u0000\u0000\u04df\u04e0\u0003n/\u0000\u04e0\u04e2"+ - "\u0001\u0000\u0000\u0000\u04e1\u04db\u0001\u0000\u0000\u0000\u04e1\u04e2"+ - "\u0001\u0000\u0000\u0000\u04e2\u04e4\u0001\u0000\u0000\u0000\u04e3\u04e5"+ - "\u0003\u011e\u0087\u0000\u04e4\u04e3\u0001\u0000\u0000\u0000\u04e5\u04e6"+ - "\u0001\u0000\u0000\u0000\u04e6\u04e4\u0001\u0000\u0000\u0000\u04e6\u04e7"+ - "\u0001\u0000\u0000\u0000\u04e7\u0121\u0001\u0000\u0000\u0000\u04e8\u04e9"+ - "\u0003\u0120\u0088\u0000\u04e9\u04ea\u0001\u0000\u0000\u0000\u04ea\u04eb"+ - "\u0006\u0089\u001d\u0000\u04eb\u0123\u0001\u0000\u0000\u0000\u04ec\u04ed"+ - "\u0003B\u0019\u0000\u04ed\u04ee\u0001\u0000\u0000\u0000\u04ee\u04ef\u0006"+ - "\u008a\u000b\u0000\u04ef\u0125\u0001\u0000\u0000\u0000\u04f0\u04f1\u0003"+ - "D\u001a\u0000\u04f1\u04f2\u0001\u0000\u0000\u0000\u04f2\u04f3\u0006\u008b"+ - "\u000b\u0000\u04f3\u0127\u0001\u0000\u0000\u0000\u04f4\u04f5\u0003F\u001b"+ - "\u0000\u04f5\u04f6\u0001\u0000\u0000\u0000\u04f6\u04f7\u0006\u008c\u000b"+ - "\u0000\u04f7\u0129\u0001\u0000\u0000\u0000\u04f8\u04f9\u0003H\u001c\u0000"+ - "\u04f9\u04fa\u0001\u0000\u0000\u0000\u04fa\u04fb\u0006\u008d\u0010\u0000"+ - "\u04fb\u04fc\u0006\u008d\f\u0000\u04fc\u04fd\u0006\u008d\f\u0000\u04fd"+ - "\u012b\u0001\u0000\u0000\u0000\u04fe\u04ff\u0003j-\u0000\u04ff\u0500\u0001"+ - "\u0000\u0000\u0000\u0500\u0501\u0006\u008e\u0014\u0000\u0501\u012d\u0001"+ - "\u0000\u0000\u0000\u0502\u0503\u0003p0\u0000\u0503\u0504\u0001\u0000\u0000"+ - "\u0000\u0504\u0505\u0006\u008f\u0013\u0000\u0505\u012f\u0001\u0000\u0000"+ - "\u0000\u0506\u0507\u0003t2\u0000\u0507\u0508\u0001\u0000\u0000\u0000\u0508"+ - "\u0509\u0006\u0090\u0017\u0000\u0509\u0131\u0001\u0000\u0000\u0000\u050a"+ - "\u050b\u0003\u011c\u0086\u0000\u050b\u050c\u0001\u0000\u0000\u0000\u050c"+ - "\u050d\u0006\u0091\u001e\u0000\u050d\u0133\u0001\u0000\u0000\u0000\u050e"+ - "\u050f\u0003\u00f8t\u0000\u050f\u0510\u0001\u0000\u0000\u0000\u0510\u0511"+ - "\u0006\u0092\u001a\u0000\u0511\u0135\u0001\u0000\u0000\u0000\u0512\u0513"+ - "\u0003\u00bcV\u0000\u0513\u0514\u0001\u0000\u0000\u0000\u0514\u0515\u0006"+ - "\u0093\u001f\u0000\u0515\u0137\u0001\u0000\u0000\u0000\u0516\u0517\u0004"+ - "\u0094\u000e\u0000\u0517\u0518\u0003\u008c>\u0000\u0518\u0519\u0001\u0000"+ - "\u0000\u0000\u0519\u051a\u0006\u0094\u0018\u0000\u051a\u0139\u0001\u0000"+ - "\u0000\u0000\u051b\u051c\u0004\u0095\u000f\u0000\u051c\u051d\u0003\u00b2"+ - "Q\u0000\u051d\u051e\u0001\u0000\u0000\u0000\u051e\u051f\u0006\u0095\u0019"+ - "\u0000\u051f\u013b\u0001\u0000\u0000\u0000\u0520\u0521\u0003B\u0019\u0000"+ - "\u0521\u0522\u0001\u0000\u0000\u0000\u0522\u0523\u0006\u0096\u000b\u0000"+ - "\u0523\u013d\u0001\u0000\u0000\u0000\u0524\u0525\u0003D\u001a\u0000\u0525"+ - "\u0526\u0001\u0000\u0000\u0000\u0526\u0527\u0006\u0097\u000b\u0000\u0527"+ - "\u013f\u0001\u0000\u0000\u0000\u0528\u0529\u0003F\u001b\u0000\u0529\u052a"+ - "\u0001\u0000\u0000\u0000\u052a\u052b\u0006\u0098\u000b\u0000\u052b\u0141"+ - "\u0001\u0000\u0000\u0000\u052c\u052d\u0003H\u001c\u0000\u052d\u052e\u0001"+ - "\u0000\u0000\u0000\u052e\u052f\u0006\u0099\u0010\u0000\u052f\u0530\u0006"+ - "\u0099\f\u0000\u0530\u0143\u0001\u0000\u0000\u0000\u0531\u0532\u0003t"+ - "2\u0000\u0532\u0533\u0001\u0000\u0000\u0000\u0533\u0534\u0006\u009a\u0017"+ - "\u0000\u0534\u0145\u0001\u0000\u0000\u0000\u0535\u0536\u0004\u009b\u0010"+ - "\u0000\u0536\u0537\u0003\u008c>\u0000\u0537\u0538\u0001\u0000\u0000\u0000"+ - "\u0538\u0539\u0006\u009b\u0018\u0000\u0539\u0147\u0001\u0000\u0000\u0000"+ - "\u053a\u053b\u0004\u009c\u0011\u0000\u053b\u053c\u0003\u00b2Q\u0000\u053c"+ - "\u053d\u0001\u0000\u0000\u0000\u053d\u053e\u0006\u009c\u0019\u0000\u053e"+ - "\u0149\u0001\u0000\u0000\u0000\u053f\u0540\u0003\u00bcV\u0000\u0540\u0541"+ - "\u0001\u0000\u0000\u0000\u0541\u0542\u0006\u009d\u001f\u0000\u0542\u014b"+ - "\u0001\u0000\u0000\u0000\u0543\u0544\u0003\u00b8T\u0000\u0544\u0545\u0001"+ - "\u0000\u0000\u0000\u0545\u0546\u0006\u009e \u0000\u0546\u014d\u0001\u0000"+ - "\u0000\u0000\u0547\u0548\u0003B\u0019\u0000\u0548\u0549\u0001\u0000\u0000"+ - "\u0000\u0549\u054a\u0006\u009f\u000b\u0000\u054a\u014f\u0001\u0000\u0000"+ - "\u0000\u054b\u054c\u0003D\u001a\u0000\u054c\u054d\u0001\u0000\u0000\u0000"+ - "\u054d\u054e\u0006\u00a0\u000b\u0000\u054e\u0151\u0001\u0000\u0000\u0000"+ - "\u054f\u0550\u0003F\u001b\u0000\u0550\u0551\u0001\u0000\u0000\u0000\u0551"+ - "\u0552\u0006\u00a1\u000b\u0000\u0552\u0153\u0001\u0000\u0000\u0000\u0553"+ - "\u0554\u0003H\u001c\u0000\u0554\u0555\u0001\u0000\u0000\u0000\u0555\u0556"+ - "\u0006\u00a2\u0010\u0000\u0556\u0557\u0006\u00a2\f\u0000\u0557\u0155\u0001"+ - "\u0000\u0000\u0000\u0558\u0559\u0007\u0001\u0000\u0000\u0559\u055a\u0007"+ - "\t\u0000\u0000\u055a\u055b\u0007\u000f\u0000\u0000\u055b\u055c\u0007\u0007"+ - "\u0000\u0000\u055c\u0157\u0001\u0000\u0000\u0000\u055d\u055e\u0003B\u0019"+ - "\u0000\u055e\u055f\u0001\u0000\u0000\u0000\u055f\u0560\u0006\u00a4\u000b"+ - "\u0000\u0560\u0159\u0001\u0000\u0000\u0000\u0561\u0562\u0003D\u001a\u0000"+ - "\u0562\u0563\u0001\u0000\u0000\u0000\u0563\u0564\u0006\u00a5\u000b\u0000"+ - "\u0564\u015b\u0001\u0000\u0000\u0000\u0565\u0566\u0003F\u001b\u0000\u0566"+ - "\u0567\u0001\u0000\u0000\u0000\u0567\u0568\u0006\u00a6\u000b\u0000\u0568"+ - "\u015d\u0001\u0000\u0000\u0000\u0569\u056a\u0003\u00b6S\u0000\u056a\u056b"+ - "\u0001\u0000\u0000\u0000\u056b\u056c\u0006\u00a7\u0011\u0000\u056c\u056d"+ - "\u0006\u00a7\f\u0000\u056d\u015f\u0001\u0000\u0000\u0000\u056e\u056f\u0003"+ - "n/\u0000\u056f\u0570\u0001\u0000\u0000\u0000\u0570\u0571\u0006\u00a8\u0012"+ - "\u0000\u0571\u0161\u0001\u0000\u0000\u0000\u0572\u0578\u0003T\"\u0000"+ - "\u0573\u0578\u0003J\u001d\u0000\u0574\u0578\u0003t2\u0000\u0575\u0578"+ - "\u0003L\u001e\u0000\u0576\u0578\u0003Z%\u0000\u0577\u0572\u0001\u0000"+ - "\u0000\u0000\u0577\u0573\u0001\u0000\u0000\u0000\u0577\u0574\u0001\u0000"+ - "\u0000\u0000\u0577\u0575\u0001\u0000\u0000\u0000\u0577\u0576\u0001\u0000"+ - "\u0000\u0000\u0578\u0579\u0001\u0000\u0000\u0000\u0579\u0577\u0001\u0000"+ - "\u0000\u0000\u0579\u057a\u0001\u0000\u0000\u0000\u057a\u0163\u0001\u0000"+ - "\u0000\u0000\u057b\u057c\u0003B\u0019\u0000\u057c\u057d\u0001\u0000\u0000"+ - "\u0000\u057d\u057e\u0006\u00aa\u000b\u0000\u057e\u0165\u0001\u0000\u0000"+ - "\u0000\u057f\u0580\u0003D\u001a\u0000\u0580\u0581\u0001\u0000\u0000\u0000"+ - "\u0581\u0582\u0006\u00ab\u000b\u0000\u0582\u0167\u0001\u0000\u0000\u0000"+ - "\u0583\u0584\u0003F\u001b\u0000\u0584\u0585\u0001\u0000\u0000\u0000\u0585"+ - "\u0586\u0006\u00ac\u000b\u0000\u0586\u0169\u0001\u0000\u0000\u0000\u0587"+ - "\u0588\u0003H\u001c\u0000\u0588\u0589\u0001\u0000\u0000\u0000\u0589\u058a"+ - "\u0006\u00ad\u0010\u0000\u058a\u058b\u0006\u00ad\f\u0000\u058b\u016b\u0001"+ - "\u0000\u0000\u0000\u058c\u058d\u0003n/\u0000\u058d\u058e\u0001\u0000\u0000"+ - "\u0000\u058e\u058f\u0006\u00ae\u0012\u0000\u058f\u016d\u0001\u0000\u0000"+ - "\u0000\u0590\u0591\u0003p0\u0000\u0591\u0592\u0001\u0000\u0000\u0000\u0592"+ - "\u0593\u0006\u00af\u0013\u0000\u0593\u016f\u0001\u0000\u0000\u0000\u0594"+ - "\u0595\u0003t2\u0000\u0595\u0596\u0001\u0000\u0000\u0000\u0596\u0597\u0006"+ - "\u00b0\u0017\u0000\u0597\u0171\u0001\u0000\u0000\u0000\u0598\u0599\u0003"+ - "\u011a\u0085\u0000\u0599\u059a\u0001\u0000\u0000\u0000\u059a\u059b\u0006"+ - "\u00b1!\u0000\u059b\u059c\u0006\u00b1\"\u0000\u059c\u0173\u0001\u0000"+ - "\u0000\u0000\u059d\u059e\u0003\u00deg\u0000\u059e\u059f\u0001\u0000\u0000"+ - "\u0000\u059f\u05a0\u0006\u00b2\u0015\u0000\u05a0\u0175\u0001\u0000\u0000"+ - "\u0000\u05a1\u05a2\u0003^\'\u0000\u05a2\u05a3\u0001\u0000\u0000\u0000"+ - "\u05a3\u05a4\u0006\u00b3\u0016\u0000\u05a4\u0177\u0001\u0000\u0000\u0000"+ - "\u05a5\u05a6\u0003B\u0019\u0000\u05a6\u05a7\u0001\u0000\u0000\u0000\u05a7"+ - "\u05a8\u0006\u00b4\u000b\u0000\u05a8\u0179\u0001\u0000\u0000\u0000\u05a9"+ - "\u05aa\u0003D\u001a\u0000\u05aa\u05ab\u0001\u0000\u0000\u0000\u05ab\u05ac"+ - "\u0006\u00b5\u000b\u0000\u05ac\u017b\u0001\u0000\u0000\u0000\u05ad\u05ae"+ - "\u0003F\u001b\u0000\u05ae\u05af\u0001\u0000\u0000\u0000\u05af\u05b0\u0006"+ - "\u00b6\u000b\u0000\u05b0\u017d\u0001\u0000\u0000\u0000\u05b1\u05b2\u0003"+ - "H\u001c\u0000\u05b2\u05b3\u0001\u0000\u0000\u0000\u05b3\u05b4\u0006\u00b7"+ - "\u0010\u0000\u05b4\u05b5\u0006\u00b7\f\u0000\u05b5\u05b6\u0006\u00b7\f"+ - "\u0000\u05b6\u017f\u0001\u0000\u0000\u0000\u05b7\u05b8\u0003p0\u0000\u05b8"+ - "\u05b9\u0001\u0000\u0000\u0000\u05b9\u05ba\u0006\u00b8\u0013\u0000\u05ba"+ - "\u0181\u0001\u0000\u0000\u0000\u05bb\u05bc\u0003t2\u0000\u05bc\u05bd\u0001"+ - "\u0000\u0000\u0000\u05bd\u05be\u0006\u00b9\u0017\u0000\u05be\u0183\u0001"+ - "\u0000\u0000\u0000\u05bf\u05c0\u0003\u00f8t\u0000\u05c0\u05c1\u0001\u0000"+ - "\u0000\u0000\u05c1\u05c2\u0006\u00ba\u001a\u0000\u05c2\u0185\u0001\u0000"+ - "\u0000\u0000\u05c3\u05c4\u0003B\u0019\u0000\u05c4\u05c5\u0001\u0000\u0000"+ - "\u0000\u05c5\u05c6\u0006\u00bb\u000b\u0000\u05c6\u0187\u0001\u0000\u0000"+ - "\u0000\u05c7\u05c8\u0003D\u001a\u0000\u05c8\u05c9\u0001\u0000\u0000\u0000"+ - "\u05c9\u05ca\u0006\u00bc\u000b\u0000\u05ca\u0189\u0001\u0000\u0000\u0000"+ - "\u05cb\u05cc\u0003F\u001b\u0000\u05cc\u05cd\u0001\u0000\u0000\u0000\u05cd"+ - "\u05ce\u0006\u00bd\u000b\u0000\u05ce\u018b\u0001\u0000\u0000\u0000\u05cf"+ - "\u05d0\u0003H\u001c\u0000\u05d0\u05d1\u0001\u0000\u0000\u0000\u05d1\u05d2"+ - "\u0006\u00be\u0010\u0000\u05d2\u05d3\u0006\u00be\f\u0000\u05d3\u018d\u0001"+ - "\u0000\u0000\u0000\u05d4\u05d5\u00036\u0013\u0000\u05d5\u05d6\u0001\u0000"+ - "\u0000\u0000\u05d6\u05d7\u0006\u00bf#\u0000\u05d7\u018f\u0001\u0000\u0000"+ - "\u0000\u05d8\u05d9\u0003\u010c~\u0000\u05d9\u05da\u0001\u0000\u0000\u0000"+ - "\u05da\u05db\u0006\u00c0$\u0000\u05db\u0191\u0001\u0000\u0000\u0000\u05dc"+ - "\u05dd\u0003\u011a\u0085\u0000\u05dd\u05de\u0001\u0000\u0000\u0000\u05de"+ - "\u05df\u0006\u00c1!\u0000\u05df\u05e0\u0006\u00c1\f\u0000\u05e0\u05e1"+ - "\u0006\u00c1\u0000\u0000\u05e1\u0193\u0001\u0000\u0000\u0000\u05e2\u05e3"+ - "\u0007\u0014\u0000\u0000\u05e3\u05e4\u0007\u0002\u0000\u0000\u05e4\u05e5"+ - "\u0007\u0001\u0000\u0000\u05e5\u05e6\u0007\t\u0000\u0000\u05e6\u05e7\u0007"+ - "\u0011\u0000\u0000\u05e7\u05e8\u0001\u0000\u0000\u0000\u05e8\u05e9\u0006"+ - "\u00c2\f\u0000\u05e9\u05ea\u0006\u00c2\u0000\u0000\u05ea\u0195\u0001\u0000"+ - "\u0000\u0000\u05eb\u05ec\u0003\u00b8T\u0000\u05ec\u05ed\u0001\u0000\u0000"+ - "\u0000\u05ed\u05ee\u0006\u00c3 \u0000\u05ee\u0197\u0001\u0000\u0000\u0000"+ - "\u05ef\u05f0\u0003\u00bcV\u0000\u05f0\u05f1\u0001\u0000\u0000\u0000\u05f1"+ - "\u05f2\u0006\u00c4\u001f\u0000\u05f2\u0199\u0001\u0000\u0000\u0000\u05f3"+ - "\u05f4\u0003B\u0019\u0000\u05f4\u05f5\u0001\u0000\u0000\u0000\u05f5\u05f6"+ - "\u0006\u00c5\u000b\u0000\u05f6\u019b\u0001\u0000\u0000\u0000\u05f7\u05f8"+ - "\u0003D\u001a\u0000\u05f8\u05f9\u0001\u0000\u0000\u0000\u05f9\u05fa\u0006"+ - "\u00c6\u000b\u0000\u05fa\u019d\u0001\u0000\u0000\u0000\u05fb\u05fc\u0003"+ - "F\u001b\u0000\u05fc\u05fd\u0001\u0000\u0000\u0000\u05fd\u05fe\u0006\u00c7"+ - "\u000b\u0000\u05fe\u019f\u0001\u0000\u0000\u0000\u05ff\u0600\u0003H\u001c"+ - "\u0000\u0600\u0601\u0001\u0000\u0000\u0000\u0601\u0602\u0006\u00c8\u0010"+ - "\u0000\u0602\u0603\u0006\u00c8\f\u0000\u0603\u01a1\u0001\u0000\u0000\u0000"+ - "\u0604\u0605\u0003\u00deg\u0000\u0605\u0606\u0001\u0000\u0000\u0000\u0606"+ - "\u0607\u0006\u00c9\u0015\u0000\u0607\u0608\u0006\u00c9\f\u0000\u0608\u0609"+ - "\u0006\u00c9%\u0000\u0609\u01a3\u0001\u0000\u0000\u0000\u060a\u060b\u0003"+ - "^\'\u0000\u060b\u060c\u0001\u0000\u0000\u0000\u060c\u060d\u0006\u00ca"+ - "\u0016\u0000\u060d\u060e\u0006\u00ca\f\u0000\u060e\u060f\u0006\u00ca%"+ - "\u0000\u060f\u01a5\u0001\u0000\u0000\u0000\u0610\u0611\u0003B\u0019\u0000"+ - "\u0611\u0612\u0001\u0000\u0000\u0000\u0612\u0613\u0006\u00cb\u000b\u0000"+ - "\u0613\u01a7\u0001\u0000\u0000\u0000\u0614\u0615\u0003D\u001a\u0000\u0615"+ - "\u0616\u0001\u0000\u0000\u0000\u0616\u0617\u0006\u00cc\u000b\u0000\u0617"+ - "\u01a9\u0001\u0000\u0000\u0000\u0618\u0619\u0003F\u001b\u0000\u0619\u061a"+ - "\u0001\u0000\u0000\u0000\u061a\u061b\u0006\u00cd\u000b\u0000\u061b\u01ab"+ - "\u0001\u0000\u0000\u0000\u061c\u061d\u0003n/\u0000\u061d\u061e\u0001\u0000"+ - "\u0000\u0000\u061e\u061f\u0006\u00ce\u0012\u0000\u061f\u0620\u0006\u00ce"+ - "\f\u0000\u0620\u0621\u0006\u00ce\t\u0000\u0621\u01ad\u0001\u0000\u0000"+ - "\u0000\u0622\u0623\u0003p0\u0000\u0623\u0624\u0001\u0000\u0000\u0000\u0624"+ - "\u0625\u0006\u00cf\u0013\u0000\u0625\u0626\u0006\u00cf\f\u0000\u0626\u0627"+ - "\u0006\u00cf\t\u0000\u0627\u01af\u0001\u0000\u0000\u0000\u0628\u0629\u0003"+ - "B\u0019\u0000\u0629\u062a\u0001\u0000\u0000\u0000\u062a\u062b\u0006\u00d0"+ - "\u000b\u0000\u062b\u01b1\u0001\u0000\u0000\u0000\u062c\u062d\u0003D\u001a"+ - "\u0000\u062d\u062e\u0001\u0000\u0000\u0000\u062e\u062f\u0006\u00d1\u000b"+ - "\u0000\u062f\u01b3\u0001\u0000\u0000\u0000\u0630\u0631\u0003F\u001b\u0000"+ - "\u0631\u0632\u0001\u0000\u0000\u0000\u0632\u0633\u0006\u00d2\u000b\u0000"+ - "\u0633\u01b5\u0001\u0000\u0000\u0000\u0634\u0635\u0003\u00bcV\u0000\u0635"+ - "\u0636\u0001\u0000\u0000\u0000\u0636\u0637\u0006\u00d3\f\u0000\u0637\u0638"+ - "\u0006\u00d3\u0000\u0000\u0638\u0639\u0006\u00d3\u001f\u0000\u0639\u01b7"+ - "\u0001\u0000\u0000\u0000\u063a\u063b\u0003\u00b8T\u0000\u063b\u063c\u0001"+ - "\u0000\u0000\u0000\u063c\u063d\u0006\u00d4\f\u0000\u063d\u063e\u0006\u00d4"+ - "\u0000\u0000\u063e\u063f\u0006\u00d4 \u0000\u063f\u01b9\u0001\u0000\u0000"+ - "\u0000\u0640\u0641\u0003d*\u0000\u0641\u0642\u0001\u0000\u0000\u0000\u0642"+ - "\u0643\u0006\u00d5\f\u0000\u0643\u0644\u0006\u00d5\u0000\u0000\u0644\u0645"+ - "\u0006\u00d5&\u0000\u0645\u01bb\u0001\u0000\u0000\u0000\u0646\u0647\u0003"+ - "H\u001c\u0000\u0647\u0648\u0001\u0000\u0000\u0000\u0648\u0649\u0006\u00d6"+ - "\u0010\u0000\u0649\u064a\u0006\u00d6\f\u0000\u064a\u01bd\u0001\u0000\u0000"+ - "\u0000B\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f"+ - "\r\u000e\u000f\u0293\u029d\u02a1\u02a4\u02ad\u02af\u02ba\u02cd\u02d2\u02db"+ - "\u02e2\u02e7\u02e9\u02f4\u02fc\u02ff\u0301\u0306\u030b\u0311\u0318\u031d"+ - "\u0323\u0326\u032e\u0332\u03b8\u03bd\u03c4\u03c6\u03d6\u03db\u03e0\u03e2"+ - "\u03e8\u0435\u043a\u046b\u046f\u0474\u0479\u047e\u0480\u0484\u0486\u04dd"+ - "\u04e1\u04e6\u0577\u0579\'\u0005\u0001\u0000\u0005\u0004\u0000\u0005\u0006"+ - "\u0000\u0005\u0002\u0000\u0005\u0003\u0000\u0005\b\u0000\u0005\u0005\u0000"+ - "\u0005\t\u0000\u0005\u000b\u0000\u0005\u000e\u0000\u0005\r\u0000\u0000"+ - "\u0001\u0000\u0004\u0000\u0000\u0007\u0010\u0000\u0007H\u0000\u0005\u0000"+ - "\u0000\u0007\u001d\u0000\u0007I\u0000\u0007&\u0000\u0007\'\u0000\u0007"+ - "$\u0000\u0007S\u0000\u0007\u001e\u0000\u0007)\u0000\u00075\u0000\u0007"+ - "G\u0000\u0007W\u0000\u0005\n\u0000\u0005\u0007\u0000\u0007a\u0000\u0007"+ - "`\u0000\u0007K\u0000\u0007J\u0000\u0007_\u0000\u0005\f\u0000\u0007\u0014"+ - "\u0000\u0007[\u0000\u0005\u000f\u0000\u0007!\u0000"; + "\u0000\u019c\u0000\u019e\u0000\u01a0z\u01a2{\u01a4|\u01a6\u0000\u01a8"+ + "\u0000\u01aa\u0000\u01ac}\u01ae~\u01b0\u007f\u01b2\u0000\u01b4\u0000\u01b6"+ + "\u0080\u01b8\u0081\u01ba\u0082\u01bc\u0000\u01be\u0000\u01c0\u0000\u01c2"+ + "\u0000\u0010\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b"+ + "\f\r\u000e\u000f$\u0002\u0000DDdd\u0002\u0000IIii\u0002\u0000SSss\u0002"+ + "\u0000EEee\u0002\u0000CCcc\u0002\u0000TTtt\u0002\u0000RRrr\u0002\u0000"+ + "OOoo\u0002\u0000PPpp\u0002\u0000NNnn\u0002\u0000HHhh\u0002\u0000VVvv\u0002"+ + "\u0000AAaa\u0002\u0000LLll\u0002\u0000XXxx\u0002\u0000FFff\u0002\u0000"+ + "MMmm\u0002\u0000GGgg\u0002\u0000KKkk\u0002\u0000WWww\u0002\u0000UUuu\u0002"+ + "\u0000JJjj\u0006\u0000\t\n\r\r //[[]]\u0002\u0000\n\n\r\r\u0003\u0000"+ + "\t\n\r\r \u0001\u000009\u0002\u0000AZaz\b\u0000\"\"NNRRTT\\\\nnrrtt\u0004"+ + "\u0000\n\n\r\r\"\"\\\\\u0002\u0000++--\u0001\u0000``\u0002\u0000BBbb\u0002"+ + "\u0000YYyy\u000b\u0000\t\n\r\r \"\",,//::==[[]]||\u0002\u0000**//\u000b"+ + "\u0000\t\n\r\r \"#,,//::<<>?\\\\||\u0678\u0000\u0010\u0001\u0000\u0000"+ + "\u0000\u0000\u0012\u0001\u0000\u0000\u0000\u0000\u0014\u0001\u0000\u0000"+ + "\u0000\u0000\u0016\u0001\u0000\u0000\u0000\u0000\u0018\u0001\u0000\u0000"+ + "\u0000\u0000\u001a\u0001\u0000\u0000\u0000\u0000\u001c\u0001\u0000\u0000"+ + "\u0000\u0000\u001e\u0001\u0000\u0000\u0000\u0000 \u0001\u0000\u0000\u0000"+ + "\u0000\"\u0001\u0000\u0000\u0000\u0000$\u0001\u0000\u0000\u0000\u0000"+ + "&\u0001\u0000\u0000\u0000\u0000(\u0001\u0000\u0000\u0000\u0000*\u0001"+ + "\u0000\u0000\u0000\u0000,\u0001\u0000\u0000\u0000\u0000.\u0001\u0000\u0000"+ + "\u0000\u00000\u0001\u0000\u0000\u0000\u00002\u0001\u0000\u0000\u0000\u0000"+ + "4\u0001\u0000\u0000\u0000\u00006\u0001\u0000\u0000\u0000\u00008\u0001"+ + "\u0000\u0000\u0000\u0000:\u0001\u0000\u0000\u0000\u0000<\u0001\u0000\u0000"+ + "\u0000\u0000>\u0001\u0000\u0000\u0000\u0000@\u0001\u0000\u0000\u0000\u0000"+ + "B\u0001\u0000\u0000\u0000\u0000D\u0001\u0000\u0000\u0000\u0000F\u0001"+ + "\u0000\u0000\u0000\u0001H\u0001\u0000\u0000\u0000\u0001^\u0001\u0000\u0000"+ + "\u0000\u0001`\u0001\u0000\u0000\u0000\u0001b\u0001\u0000\u0000\u0000\u0001"+ + "d\u0001\u0000\u0000\u0000\u0001f\u0001\u0000\u0000\u0000\u0001h\u0001"+ + "\u0000\u0000\u0000\u0001j\u0001\u0000\u0000\u0000\u0001l\u0001\u0000\u0000"+ + "\u0000\u0001n\u0001\u0000\u0000\u0000\u0001p\u0001\u0000\u0000\u0000\u0001"+ + "r\u0001\u0000\u0000\u0000\u0001t\u0001\u0000\u0000\u0000\u0001v\u0001"+ + "\u0000\u0000\u0000\u0001x\u0001\u0000\u0000\u0000\u0001z\u0001\u0000\u0000"+ + "\u0000\u0001|\u0001\u0000\u0000\u0000\u0001~\u0001\u0000\u0000\u0000\u0001"+ + "\u0080\u0001\u0000\u0000\u0000\u0001\u0082\u0001\u0000\u0000\u0000\u0001"+ + "\u0084\u0001\u0000\u0000\u0000\u0001\u0086\u0001\u0000\u0000\u0000\u0001"+ + "\u0088\u0001\u0000\u0000\u0000\u0001\u008a\u0001\u0000\u0000\u0000\u0001"+ + "\u008c\u0001\u0000\u0000\u0000\u0001\u008e\u0001\u0000\u0000\u0000\u0001"+ + "\u0090\u0001\u0000\u0000\u0000\u0001\u0092\u0001\u0000\u0000\u0000\u0001"+ + "\u0094\u0001\u0000\u0000\u0000\u0001\u0096\u0001\u0000\u0000\u0000\u0001"+ + "\u0098\u0001\u0000\u0000\u0000\u0001\u009a\u0001\u0000\u0000\u0000\u0001"+ + "\u009c\u0001\u0000\u0000\u0000\u0001\u009e\u0001\u0000\u0000\u0000\u0001"+ + "\u00a0\u0001\u0000\u0000\u0000\u0001\u00a2\u0001\u0000\u0000\u0000\u0001"+ + "\u00a4\u0001\u0000\u0000\u0000\u0001\u00a6\u0001\u0000\u0000\u0000\u0001"+ + "\u00a8\u0001\u0000\u0000\u0000\u0001\u00aa\u0001\u0000\u0000\u0000\u0001"+ + "\u00ac\u0001\u0000\u0000\u0000\u0001\u00ae\u0001\u0000\u0000\u0000\u0001"+ + "\u00b0\u0001\u0000\u0000\u0000\u0001\u00b2\u0001\u0000\u0000\u0000\u0001"+ + "\u00b4\u0001\u0000\u0000\u0000\u0001\u00b6\u0001\u0000\u0000\u0000\u0001"+ + "\u00b8\u0001\u0000\u0000\u0000\u0001\u00bc\u0001\u0000\u0000\u0000\u0001"+ + "\u00be\u0001\u0000\u0000\u0000\u0001\u00c0\u0001\u0000\u0000\u0000\u0001"+ + "\u00c2\u0001\u0000\u0000\u0000\u0002\u00c4\u0001\u0000\u0000\u0000\u0002"+ + "\u00c6\u0001\u0000\u0000\u0000\u0002\u00c8\u0001\u0000\u0000\u0000\u0002"+ + "\u00ca\u0001\u0000\u0000\u0000\u0002\u00cc\u0001\u0000\u0000\u0000\u0003"+ + "\u00ce\u0001\u0000\u0000\u0000\u0003\u00d0\u0001\u0000\u0000\u0000\u0003"+ + "\u00d2\u0001\u0000\u0000\u0000\u0003\u00d4\u0001\u0000\u0000\u0000\u0003"+ + "\u00d6\u0001\u0000\u0000\u0000\u0003\u00d8\u0001\u0000\u0000\u0000\u0003"+ + "\u00da\u0001\u0000\u0000\u0000\u0003\u00de\u0001\u0000\u0000\u0000\u0003"+ + "\u00e0\u0001\u0000\u0000\u0000\u0003\u00e2\u0001\u0000\u0000\u0000\u0003"+ + "\u00e4\u0001\u0000\u0000\u0000\u0003\u00e6\u0001\u0000\u0000\u0000\u0003"+ + "\u00e8\u0001\u0000\u0000\u0000\u0004\u00ea\u0001\u0000\u0000\u0000\u0004"+ + "\u00ec\u0001\u0000\u0000\u0000\u0004\u00ee\u0001\u0000\u0000\u0000\u0004"+ + "\u00f0\u0001\u0000\u0000\u0000\u0004\u00f2\u0001\u0000\u0000\u0000\u0004"+ + "\u00f8\u0001\u0000\u0000\u0000\u0004\u00fa\u0001\u0000\u0000\u0000\u0004"+ + "\u00fc\u0001\u0000\u0000\u0000\u0004\u00fe\u0001\u0000\u0000\u0000\u0005"+ + "\u0100\u0001\u0000\u0000\u0000\u0005\u0102\u0001\u0000\u0000\u0000\u0005"+ + "\u0104\u0001\u0000\u0000\u0000\u0005\u0106\u0001\u0000\u0000\u0000\u0005"+ + "\u0108\u0001\u0000\u0000\u0000\u0005\u010a\u0001\u0000\u0000\u0000\u0005"+ + "\u010c\u0001\u0000\u0000\u0000\u0005\u010e\u0001\u0000\u0000\u0000\u0005"+ + "\u0110\u0001\u0000\u0000\u0000\u0005\u0112\u0001\u0000\u0000\u0000\u0005"+ + "\u0114\u0001\u0000\u0000\u0000\u0006\u0116\u0001\u0000\u0000\u0000\u0006"+ + "\u0118\u0001\u0000\u0000\u0000\u0006\u011a\u0001\u0000\u0000\u0000\u0006"+ + "\u011c\u0001\u0000\u0000\u0000\u0006\u0120\u0001\u0000\u0000\u0000\u0006"+ + "\u0122\u0001\u0000\u0000\u0000\u0006\u0124\u0001\u0000\u0000\u0000\u0006"+ + "\u0126\u0001\u0000\u0000\u0000\u0006\u0128\u0001\u0000\u0000\u0000\u0007"+ + "\u012a\u0001\u0000\u0000\u0000\u0007\u012c\u0001\u0000\u0000\u0000\u0007"+ + "\u012e\u0001\u0000\u0000\u0000\u0007\u0130\u0001\u0000\u0000\u0000\u0007"+ + "\u0132\u0001\u0000\u0000\u0000\u0007\u0134\u0001\u0000\u0000\u0000\u0007"+ + "\u0136\u0001\u0000\u0000\u0000\u0007\u0138\u0001\u0000\u0000\u0000\u0007"+ + "\u013a\u0001\u0000\u0000\u0000\u0007\u013c\u0001\u0000\u0000\u0000\u0007"+ + "\u013e\u0001\u0000\u0000\u0000\u0007\u0140\u0001\u0000\u0000\u0000\b\u0142"+ + "\u0001\u0000\u0000\u0000\b\u0144\u0001\u0000\u0000\u0000\b\u0146\u0001"+ + "\u0000\u0000\u0000\b\u0148\u0001\u0000\u0000\u0000\b\u014a\u0001\u0000"+ + "\u0000\u0000\b\u014c\u0001\u0000\u0000\u0000\b\u014e\u0001\u0000\u0000"+ + "\u0000\b\u0150\u0001\u0000\u0000\u0000\b\u0152\u0001\u0000\u0000\u0000"+ + "\t\u0154\u0001\u0000\u0000\u0000\t\u0156\u0001\u0000\u0000\u0000\t\u0158"+ + "\u0001\u0000\u0000\u0000\t\u015a\u0001\u0000\u0000\u0000\t\u015c\u0001"+ + "\u0000\u0000\u0000\n\u015e\u0001\u0000\u0000\u0000\n\u0160\u0001\u0000"+ + "\u0000\u0000\n\u0162\u0001\u0000\u0000\u0000\n\u0164\u0001\u0000\u0000"+ + "\u0000\n\u0166\u0001\u0000\u0000\u0000\n\u0168\u0001\u0000\u0000\u0000"+ + "\u000b\u016a\u0001\u0000\u0000\u0000\u000b\u016c\u0001\u0000\u0000\u0000"+ + "\u000b\u016e\u0001\u0000\u0000\u0000\u000b\u0170\u0001\u0000\u0000\u0000"+ + "\u000b\u0172\u0001\u0000\u0000\u0000\u000b\u0174\u0001\u0000\u0000\u0000"+ + "\u000b\u0176\u0001\u0000\u0000\u0000\u000b\u0178\u0001\u0000\u0000\u0000"+ + "\u000b\u017a\u0001\u0000\u0000\u0000\u000b\u017c\u0001\u0000\u0000\u0000"+ + "\f\u017e\u0001\u0000\u0000\u0000\f\u0180\u0001\u0000\u0000\u0000\f\u0182"+ + "\u0001\u0000\u0000\u0000\f\u0184\u0001\u0000\u0000\u0000\f\u0186\u0001"+ + "\u0000\u0000\u0000\f\u0188\u0001\u0000\u0000\u0000\f\u018a\u0001\u0000"+ + "\u0000\u0000\r\u018c\u0001\u0000\u0000\u0000\r\u018e\u0001\u0000\u0000"+ + "\u0000\r\u0190\u0001\u0000\u0000\u0000\r\u0192\u0001\u0000\u0000\u0000"+ + "\r\u0194\u0001\u0000\u0000\u0000\r\u0196\u0001\u0000\u0000\u0000\r\u0198"+ + "\u0001\u0000\u0000\u0000\r\u019a\u0001\u0000\u0000\u0000\r\u019c\u0001"+ + "\u0000\u0000\u0000\r\u019e\u0001\u0000\u0000\u0000\r\u01a0\u0001\u0000"+ + "\u0000\u0000\r\u01a2\u0001\u0000\u0000\u0000\r\u01a4\u0001\u0000\u0000"+ + "\u0000\u000e\u01a6\u0001\u0000\u0000\u0000\u000e\u01a8\u0001\u0000\u0000"+ + "\u0000\u000e\u01aa\u0001\u0000\u0000\u0000\u000e\u01ac\u0001\u0000\u0000"+ + "\u0000\u000e\u01ae\u0001\u0000\u0000\u0000\u000e\u01b0\u0001\u0000\u0000"+ + "\u0000\u000f\u01b2\u0001\u0000\u0000\u0000\u000f\u01b4\u0001\u0000\u0000"+ + "\u0000\u000f\u01b6\u0001\u0000\u0000\u0000\u000f\u01b8\u0001\u0000\u0000"+ + "\u0000\u000f\u01ba\u0001\u0000\u0000\u0000\u000f\u01bc\u0001\u0000\u0000"+ + "\u0000\u000f\u01be\u0001\u0000\u0000\u0000\u000f\u01c0\u0001\u0000\u0000"+ + "\u0000\u000f\u01c2\u0001\u0000\u0000\u0000\u0010\u01c4\u0001\u0000\u0000"+ + "\u0000\u0012\u01ce\u0001\u0000\u0000\u0000\u0014\u01d5\u0001\u0000\u0000"+ + "\u0000\u0016\u01de\u0001\u0000\u0000\u0000\u0018\u01e5\u0001\u0000\u0000"+ + "\u0000\u001a\u01ef\u0001\u0000\u0000\u0000\u001c\u01f6\u0001\u0000\u0000"+ + "\u0000\u001e\u01fd\u0001\u0000\u0000\u0000 \u0204\u0001\u0000\u0000\u0000"+ + "\"\u020c\u0001\u0000\u0000\u0000$\u0218\u0001\u0000\u0000\u0000&\u0221"+ + "\u0001\u0000\u0000\u0000(\u0227\u0001\u0000\u0000\u0000*\u022e\u0001\u0000"+ + "\u0000\u0000,\u0235\u0001\u0000\u0000\u0000.\u023d\u0001\u0000\u0000\u0000"+ + "0\u0245\u0001\u0000\u0000\u00002\u0254\u0001\u0000\u0000\u00004\u0260"+ + "\u0001\u0000\u0000\u00006\u026b\u0001\u0000\u0000\u00008\u0273\u0001\u0000"+ + "\u0000\u0000:\u027b\u0001\u0000\u0000\u0000<\u0283\u0001\u0000\u0000\u0000"+ + ">\u028c\u0001\u0000\u0000\u0000@\u0297\u0001\u0000\u0000\u0000B\u029d"+ + "\u0001\u0000\u0000\u0000D\u02ae\u0001\u0000\u0000\u0000F\u02be\u0001\u0000"+ + "\u0000\u0000H\u02c4\u0001\u0000\u0000\u0000J\u02c8\u0001\u0000\u0000\u0000"+ + "L\u02ca\u0001\u0000\u0000\u0000N\u02cc\u0001\u0000\u0000\u0000P\u02cf"+ + "\u0001\u0000\u0000\u0000R\u02d1\u0001\u0000\u0000\u0000T\u02da\u0001\u0000"+ + "\u0000\u0000V\u02dc\u0001\u0000\u0000\u0000X\u02e1\u0001\u0000\u0000\u0000"+ + "Z\u02e3\u0001\u0000\u0000\u0000\\\u02e8\u0001\u0000\u0000\u0000^\u0307"+ + "\u0001\u0000\u0000\u0000`\u030a\u0001\u0000\u0000\u0000b\u0338\u0001\u0000"+ + "\u0000\u0000d\u033a\u0001\u0000\u0000\u0000f\u033d\u0001\u0000\u0000\u0000"+ + "h\u0341\u0001\u0000\u0000\u0000j\u0345\u0001\u0000\u0000\u0000l\u0347"+ + "\u0001\u0000\u0000\u0000n\u034a\u0001\u0000\u0000\u0000p\u034c\u0001\u0000"+ + "\u0000\u0000r\u034e\u0001\u0000\u0000\u0000t\u0353\u0001\u0000\u0000\u0000"+ + "v\u0355\u0001\u0000\u0000\u0000x\u035b\u0001\u0000\u0000\u0000z\u0361"+ + "\u0001\u0000\u0000\u0000|\u0364\u0001\u0000\u0000\u0000~\u0367\u0001\u0000"+ + "\u0000\u0000\u0080\u036c\u0001\u0000\u0000\u0000\u0082\u0371\u0001\u0000"+ + "\u0000\u0000\u0084\u0373\u0001\u0000\u0000\u0000\u0086\u0377\u0001\u0000"+ + "\u0000\u0000\u0088\u037c\u0001\u0000\u0000\u0000\u008a\u0382\u0001\u0000"+ + "\u0000\u0000\u008c\u0385\u0001\u0000\u0000\u0000\u008e\u0387\u0001\u0000"+ + "\u0000\u0000\u0090\u038d\u0001\u0000\u0000\u0000\u0092\u038f\u0001\u0000"+ + "\u0000\u0000\u0094\u0394\u0001\u0000\u0000\u0000\u0096\u0397\u0001\u0000"+ + "\u0000\u0000\u0098\u039a\u0001\u0000\u0000\u0000\u009a\u039d\u0001\u0000"+ + "\u0000\u0000\u009c\u039f\u0001\u0000\u0000\u0000\u009e\u03a2\u0001\u0000"+ + "\u0000\u0000\u00a0\u03a4\u0001\u0000\u0000\u0000\u00a2\u03a7\u0001\u0000"+ + "\u0000\u0000\u00a4\u03a9\u0001\u0000\u0000\u0000\u00a6\u03ab\u0001\u0000"+ + "\u0000\u0000\u00a8\u03ad\u0001\u0000\u0000\u0000\u00aa\u03af\u0001\u0000"+ + "\u0000\u0000\u00ac\u03b1\u0001\u0000\u0000\u0000\u00ae\u03b4\u0001\u0000"+ + "\u0000\u0000\u00b0\u03b7\u0001\u0000\u0000\u0000\u00b2\u03cc\u0001\u0000"+ + "\u0000\u0000\u00b4\u03ce\u0001\u0000\u0000\u0000\u00b6\u03d3\u0001\u0000"+ + "\u0000\u0000\u00b8\u03e8\u0001\u0000\u0000\u0000\u00ba\u03ea\u0001\u0000"+ + "\u0000\u0000\u00bc\u03f2\u0001\u0000\u0000\u0000\u00be\u03f4\u0001\u0000"+ + "\u0000\u0000\u00c0\u03f8\u0001\u0000\u0000\u0000\u00c2\u03fc\u0001\u0000"+ + "\u0000\u0000\u00c4\u0400\u0001\u0000\u0000\u0000\u00c6\u0405\u0001\u0000"+ + "\u0000\u0000\u00c8\u040a\u0001\u0000\u0000\u0000\u00ca\u040e\u0001\u0000"+ + "\u0000\u0000\u00cc\u0412\u0001\u0000\u0000\u0000\u00ce\u0416\u0001\u0000"+ + "\u0000\u0000\u00d0\u041b\u0001\u0000\u0000\u0000\u00d2\u041f\u0001\u0000"+ + "\u0000\u0000\u00d4\u0423\u0001\u0000\u0000\u0000\u00d6\u0427\u0001\u0000"+ + "\u0000\u0000\u00d8\u042b\u0001\u0000\u0000\u0000\u00da\u042f\u0001\u0000"+ + "\u0000\u0000\u00dc\u043b\u0001\u0000\u0000\u0000\u00de\u043e\u0001\u0000"+ + "\u0000\u0000\u00e0\u0442\u0001\u0000\u0000\u0000\u00e2\u0446\u0001\u0000"+ + "\u0000\u0000\u00e4\u044a\u0001\u0000\u0000\u0000\u00e6\u044e\u0001\u0000"+ + "\u0000\u0000\u00e8\u0452\u0001\u0000\u0000\u0000\u00ea\u0456\u0001\u0000"+ + "\u0000\u0000\u00ec\u045b\u0001\u0000\u0000\u0000\u00ee\u045f\u0001\u0000"+ + "\u0000\u0000\u00f0\u0463\u0001\u0000\u0000\u0000\u00f2\u0468\u0001\u0000"+ + "\u0000\u0000\u00f4\u0471\u0001\u0000\u0000\u0000\u00f6\u0486\u0001\u0000"+ + "\u0000\u0000\u00f8\u048a\u0001\u0000\u0000\u0000\u00fa\u048e\u0001\u0000"+ + "\u0000\u0000\u00fc\u0492\u0001\u0000\u0000\u0000\u00fe\u0496\u0001\u0000"+ + "\u0000\u0000\u0100\u049a\u0001\u0000\u0000\u0000\u0102\u049f\u0001\u0000"+ + "\u0000\u0000\u0104\u04a3\u0001\u0000\u0000\u0000\u0106\u04a7\u0001\u0000"+ + "\u0000\u0000\u0108\u04ab\u0001\u0000\u0000\u0000\u010a\u04b0\u0001\u0000"+ + "\u0000\u0000\u010c\u04b5\u0001\u0000\u0000\u0000\u010e\u04b8\u0001\u0000"+ + "\u0000\u0000\u0110\u04bc\u0001\u0000\u0000\u0000\u0112\u04c0\u0001\u0000"+ + "\u0000\u0000\u0114\u04c4\u0001\u0000\u0000\u0000\u0116\u04c8\u0001\u0000"+ + "\u0000\u0000\u0118\u04cd\u0001\u0000\u0000\u0000\u011a\u04d2\u0001\u0000"+ + "\u0000\u0000\u011c\u04d7\u0001\u0000\u0000\u0000\u011e\u04de\u0001\u0000"+ + "\u0000\u0000\u0120\u04e7\u0001\u0000\u0000\u0000\u0122\u04ee\u0001\u0000"+ + "\u0000\u0000\u0124\u04f2\u0001\u0000\u0000\u0000\u0126\u04f6\u0001\u0000"+ + "\u0000\u0000\u0128\u04fa\u0001\u0000\u0000\u0000\u012a\u04fe\u0001\u0000"+ + "\u0000\u0000\u012c\u0504\u0001\u0000\u0000\u0000\u012e\u0508\u0001\u0000"+ + "\u0000\u0000\u0130\u050c\u0001\u0000\u0000\u0000\u0132\u0510\u0001\u0000"+ + "\u0000\u0000\u0134\u0514\u0001\u0000\u0000\u0000\u0136\u0518\u0001\u0000"+ + "\u0000\u0000\u0138\u051c\u0001\u0000\u0000\u0000\u013a\u0521\u0001\u0000"+ + "\u0000\u0000\u013c\u0526\u0001\u0000\u0000\u0000\u013e\u052a\u0001\u0000"+ + "\u0000\u0000\u0140\u052e\u0001\u0000\u0000\u0000\u0142\u0532\u0001\u0000"+ + "\u0000\u0000\u0144\u0537\u0001\u0000\u0000\u0000\u0146\u053b\u0001\u0000"+ + "\u0000\u0000\u0148\u0540\u0001\u0000\u0000\u0000\u014a\u0545\u0001\u0000"+ + "\u0000\u0000\u014c\u0549\u0001\u0000\u0000\u0000\u014e\u054d\u0001\u0000"+ + "\u0000\u0000\u0150\u0551\u0001\u0000\u0000\u0000\u0152\u0555\u0001\u0000"+ + "\u0000\u0000\u0154\u0559\u0001\u0000\u0000\u0000\u0156\u055e\u0001\u0000"+ + "\u0000\u0000\u0158\u0563\u0001\u0000\u0000\u0000\u015a\u0567\u0001\u0000"+ + "\u0000\u0000\u015c\u056b\u0001\u0000\u0000\u0000\u015e\u056f\u0001\u0000"+ + "\u0000\u0000\u0160\u0574\u0001\u0000\u0000\u0000\u0162\u057d\u0001\u0000"+ + "\u0000\u0000\u0164\u0581\u0001\u0000\u0000\u0000\u0166\u0585\u0001\u0000"+ + "\u0000\u0000\u0168\u0589\u0001\u0000\u0000\u0000\u016a\u058d\u0001\u0000"+ + "\u0000\u0000\u016c\u0592\u0001\u0000\u0000\u0000\u016e\u0596\u0001\u0000"+ + "\u0000\u0000\u0170\u059a\u0001\u0000\u0000\u0000\u0172\u059e\u0001\u0000"+ + "\u0000\u0000\u0174\u05a3\u0001\u0000\u0000\u0000\u0176\u05a7\u0001\u0000"+ + "\u0000\u0000\u0178\u05ab\u0001\u0000\u0000\u0000\u017a\u05af\u0001\u0000"+ + "\u0000\u0000\u017c\u05b3\u0001\u0000\u0000\u0000\u017e\u05b7\u0001\u0000"+ + "\u0000\u0000\u0180\u05bd\u0001\u0000\u0000\u0000\u0182\u05c1\u0001\u0000"+ + "\u0000\u0000\u0184\u05c5\u0001\u0000\u0000\u0000\u0186\u05c9\u0001\u0000"+ + "\u0000\u0000\u0188\u05cd\u0001\u0000\u0000\u0000\u018a\u05d1\u0001\u0000"+ + "\u0000\u0000\u018c\u05d5\u0001\u0000\u0000\u0000\u018e\u05da\u0001\u0000"+ + "\u0000\u0000\u0190\u05de\u0001\u0000\u0000\u0000\u0192\u05e2\u0001\u0000"+ + "\u0000\u0000\u0194\u05e8\u0001\u0000\u0000\u0000\u0196\u05f1\u0001\u0000"+ + "\u0000\u0000\u0198\u05f5\u0001\u0000\u0000\u0000\u019a\u05f9\u0001\u0000"+ + "\u0000\u0000\u019c\u05fd\u0001\u0000\u0000\u0000\u019e\u0601\u0001\u0000"+ + "\u0000\u0000\u01a0\u0605\u0001\u0000\u0000\u0000\u01a2\u0609\u0001\u0000"+ + "\u0000\u0000\u01a4\u060d\u0001\u0000\u0000\u0000\u01a6\u0611\u0001\u0000"+ + "\u0000\u0000\u01a8\u0616\u0001\u0000\u0000\u0000\u01aa\u061c\u0001\u0000"+ + "\u0000\u0000\u01ac\u0622\u0001\u0000\u0000\u0000\u01ae\u0626\u0001\u0000"+ + "\u0000\u0000\u01b0\u062a\u0001\u0000\u0000\u0000\u01b2\u062e\u0001\u0000"+ + "\u0000\u0000\u01b4\u0634\u0001\u0000\u0000\u0000\u01b6\u063a\u0001\u0000"+ + "\u0000\u0000\u01b8\u063e\u0001\u0000\u0000\u0000\u01ba\u0642\u0001\u0000"+ + "\u0000\u0000\u01bc\u0646\u0001\u0000\u0000\u0000\u01be\u064c\u0001\u0000"+ + "\u0000\u0000\u01c0\u0652\u0001\u0000\u0000\u0000\u01c2\u0658\u0001\u0000"+ + "\u0000\u0000\u01c4\u01c5\u0007\u0000\u0000\u0000\u01c5\u01c6\u0007\u0001"+ + "\u0000\u0000\u01c6\u01c7\u0007\u0002\u0000\u0000\u01c7\u01c8\u0007\u0002"+ + "\u0000\u0000\u01c8\u01c9\u0007\u0003\u0000\u0000\u01c9\u01ca\u0007\u0004"+ + "\u0000\u0000\u01ca\u01cb\u0007\u0005\u0000\u0000\u01cb\u01cc\u0001\u0000"+ + "\u0000\u0000\u01cc\u01cd\u0006\u0000\u0000\u0000\u01cd\u0011\u0001\u0000"+ + "\u0000\u0000\u01ce\u01cf\u0007\u0000\u0000\u0000\u01cf\u01d0\u0007\u0006"+ + "\u0000\u0000\u01d0\u01d1\u0007\u0007\u0000\u0000\u01d1\u01d2\u0007\b\u0000"+ + "\u0000\u01d2\u01d3\u0001\u0000\u0000\u0000\u01d3\u01d4\u0006\u0001\u0001"+ + "\u0000\u01d4\u0013\u0001\u0000\u0000\u0000\u01d5\u01d6\u0007\u0003\u0000"+ + "\u0000\u01d6\u01d7\u0007\t\u0000\u0000\u01d7\u01d8\u0007\u0006\u0000\u0000"+ + "\u01d8\u01d9\u0007\u0001\u0000\u0000\u01d9\u01da\u0007\u0004\u0000\u0000"+ + "\u01da\u01db\u0007\n\u0000\u0000\u01db\u01dc\u0001\u0000\u0000\u0000\u01dc"+ + "\u01dd\u0006\u0002\u0002\u0000\u01dd\u0015\u0001\u0000\u0000\u0000\u01de"+ + "\u01df\u0007\u0003\u0000\u0000\u01df\u01e0\u0007\u000b\u0000\u0000\u01e0"+ + "\u01e1\u0007\f\u0000\u0000\u01e1\u01e2\u0007\r\u0000\u0000\u01e2\u01e3"+ + "\u0001\u0000\u0000\u0000\u01e3\u01e4\u0006\u0003\u0000\u0000\u01e4\u0017"+ + "\u0001\u0000\u0000\u0000\u01e5\u01e6\u0007\u0003\u0000\u0000\u01e6\u01e7"+ + "\u0007\u000e\u0000\u0000\u01e7\u01e8\u0007\b\u0000\u0000\u01e8\u01e9\u0007"+ + "\r\u0000\u0000\u01e9\u01ea\u0007\f\u0000\u0000\u01ea\u01eb\u0007\u0001"+ + "\u0000\u0000\u01eb\u01ec\u0007\t\u0000\u0000\u01ec\u01ed\u0001\u0000\u0000"+ + "\u0000\u01ed\u01ee\u0006\u0004\u0003\u0000\u01ee\u0019\u0001\u0000\u0000"+ + "\u0000\u01ef\u01f0\u0007\u000f\u0000\u0000\u01f0\u01f1\u0007\u0006\u0000"+ + "\u0000\u01f1\u01f2\u0007\u0007\u0000\u0000\u01f2\u01f3\u0007\u0010\u0000"+ + "\u0000\u01f3\u01f4\u0001\u0000\u0000\u0000\u01f4\u01f5\u0006\u0005\u0004"+ + "\u0000\u01f5\u001b\u0001\u0000\u0000\u0000\u01f6\u01f7\u0007\u0011\u0000"+ + "\u0000\u01f7\u01f8\u0007\u0006\u0000\u0000\u01f8\u01f9\u0007\u0007\u0000"+ + "\u0000\u01f9\u01fa\u0007\u0012\u0000\u0000\u01fa\u01fb\u0001\u0000\u0000"+ + "\u0000\u01fb\u01fc\u0006\u0006\u0000\u0000\u01fc\u001d\u0001\u0000\u0000"+ + "\u0000\u01fd\u01fe\u0007\u0012\u0000\u0000\u01fe\u01ff\u0007\u0003\u0000"+ + "\u0000\u01ff\u0200\u0007\u0003\u0000\u0000\u0200\u0201\u0007\b\u0000\u0000"+ + "\u0201\u0202\u0001\u0000\u0000\u0000\u0202\u0203\u0006\u0007\u0001\u0000"+ + "\u0203\u001f\u0001\u0000\u0000\u0000\u0204\u0205\u0007\r\u0000\u0000\u0205"+ + "\u0206\u0007\u0001\u0000\u0000\u0206\u0207\u0007\u0010\u0000\u0000\u0207"+ + "\u0208\u0007\u0001\u0000\u0000\u0208\u0209\u0007\u0005\u0000\u0000\u0209"+ + "\u020a\u0001\u0000\u0000\u0000\u020a\u020b\u0006\b\u0000\u0000\u020b!"+ + "\u0001\u0000\u0000\u0000\u020c\u020d\u0007\u0010\u0000\u0000\u020d\u020e"+ + "\u0007\u000b\u0000\u0000\u020e\u020f\u0005_\u0000\u0000\u020f\u0210\u0007"+ + "\u0003\u0000\u0000\u0210\u0211\u0007\u000e\u0000\u0000\u0211\u0212\u0007"+ + "\b\u0000\u0000\u0212\u0213\u0007\f\u0000\u0000\u0213\u0214\u0007\t\u0000"+ + "\u0000\u0214\u0215\u0007\u0000\u0000\u0000\u0215\u0216\u0001\u0000\u0000"+ + "\u0000\u0216\u0217\u0006\t\u0005\u0000\u0217#\u0001\u0000\u0000\u0000"+ + "\u0218\u0219\u0007\u0006\u0000\u0000\u0219\u021a\u0007\u0003\u0000\u0000"+ + "\u021a\u021b\u0007\t\u0000\u0000\u021b\u021c\u0007\f\u0000\u0000\u021c"+ + "\u021d\u0007\u0010\u0000\u0000\u021d\u021e\u0007\u0003\u0000\u0000\u021e"+ + "\u021f\u0001\u0000\u0000\u0000\u021f\u0220\u0006\n\u0006\u0000\u0220%"+ + "\u0001\u0000\u0000\u0000\u0221\u0222\u0007\u0006\u0000\u0000\u0222\u0223"+ + "\u0007\u0007\u0000\u0000\u0223\u0224\u0007\u0013\u0000\u0000\u0224\u0225"+ + "\u0001\u0000\u0000\u0000\u0225\u0226\u0006\u000b\u0000\u0000\u0226\'\u0001"+ + "\u0000\u0000\u0000\u0227\u0228\u0007\u0002\u0000\u0000\u0228\u0229\u0007"+ + "\n\u0000\u0000\u0229\u022a\u0007\u0007\u0000\u0000\u022a\u022b\u0007\u0013"+ + "\u0000\u0000\u022b\u022c\u0001\u0000\u0000\u0000\u022c\u022d\u0006\f\u0007"+ + "\u0000\u022d)\u0001\u0000\u0000\u0000\u022e\u022f\u0007\u0002\u0000\u0000"+ + "\u022f\u0230\u0007\u0007\u0000\u0000\u0230\u0231\u0007\u0006\u0000\u0000"+ + "\u0231\u0232\u0007\u0005\u0000\u0000\u0232\u0233\u0001\u0000\u0000\u0000"+ + "\u0233\u0234\u0006\r\u0000\u0000\u0234+\u0001\u0000\u0000\u0000\u0235"+ + "\u0236\u0007\u0002\u0000\u0000\u0236\u0237\u0007\u0005\u0000\u0000\u0237"+ + "\u0238\u0007\f\u0000\u0000\u0238\u0239\u0007\u0005\u0000\u0000\u0239\u023a"+ + "\u0007\u0002\u0000\u0000\u023a\u023b\u0001\u0000\u0000\u0000\u023b\u023c"+ + "\u0006\u000e\u0000\u0000\u023c-\u0001\u0000\u0000\u0000\u023d\u023e\u0007"+ + "\u0013\u0000\u0000\u023e\u023f\u0007\n\u0000\u0000\u023f\u0240\u0007\u0003"+ + "\u0000\u0000\u0240\u0241\u0007\u0006\u0000\u0000\u0241\u0242\u0007\u0003"+ + "\u0000\u0000\u0242\u0243\u0001\u0000\u0000\u0000\u0243\u0244\u0006\u000f"+ + "\u0000\u0000\u0244/\u0001\u0000\u0000\u0000\u0245\u0246\u0004\u0010\u0000"+ + "\u0000\u0246\u0247\u0007\u0001\u0000\u0000\u0247\u0248\u0007\t\u0000\u0000"+ + "\u0248\u0249\u0007\r\u0000\u0000\u0249\u024a\u0007\u0001\u0000\u0000\u024a"+ + "\u024b\u0007\t\u0000\u0000\u024b\u024c\u0007\u0003\u0000\u0000\u024c\u024d"+ + "\u0007\u0002\u0000\u0000\u024d\u024e\u0007\u0005\u0000\u0000\u024e\u024f"+ + "\u0007\f\u0000\u0000\u024f\u0250\u0007\u0005\u0000\u0000\u0250\u0251\u0007"+ + "\u0002\u0000\u0000\u0251\u0252\u0001\u0000\u0000\u0000\u0252\u0253\u0006"+ + "\u0010\u0000\u0000\u02531\u0001\u0000\u0000\u0000\u0254\u0255\u0004\u0011"+ + "\u0001\u0000\u0255\u0256\u0007\r\u0000\u0000\u0256\u0257\u0007\u0007\u0000"+ + "\u0000\u0257\u0258\u0007\u0007\u0000\u0000\u0258\u0259\u0007\u0012\u0000"+ + "\u0000\u0259\u025a\u0007\u0014\u0000\u0000\u025a\u025b\u0007\b\u0000\u0000"+ + "\u025b\u025c\u0005_\u0000\u0000\u025c\u025d\u0005\u8001\uf414\u0000\u0000"+ + "\u025d\u025e\u0001\u0000\u0000\u0000\u025e\u025f\u0006\u0011\b\u0000\u025f"+ + "3\u0001\u0000\u0000\u0000\u0260\u0261\u0004\u0012\u0002\u0000\u0261\u0262"+ + "\u0007\u0010\u0000\u0000\u0262\u0263\u0007\u0003\u0000\u0000\u0263\u0264"+ + "\u0007\u0005\u0000\u0000\u0264\u0265\u0007\u0006\u0000\u0000\u0265\u0266"+ + "\u0007\u0001\u0000\u0000\u0266\u0267\u0007\u0004\u0000\u0000\u0267\u0268"+ + "\u0007\u0002\u0000\u0000\u0268\u0269\u0001\u0000\u0000\u0000\u0269\u026a"+ + "\u0006\u0012\t\u0000\u026a5\u0001\u0000\u0000\u0000\u026b\u026c\u0004"+ + "\u0013\u0003\u0000\u026c\u026d\u0007\u0015\u0000\u0000\u026d\u026e\u0007"+ + "\u0007\u0000\u0000\u026e\u026f\u0007\u0001\u0000\u0000\u026f\u0270\u0007"+ + "\t\u0000\u0000\u0270\u0271\u0001\u0000\u0000\u0000\u0271\u0272\u0006\u0013"+ + "\n\u0000\u02727\u0001\u0000\u0000\u0000\u0273\u0274\u0004\u0014\u0004"+ + "\u0000\u0274\u0275\u0007\u000f\u0000\u0000\u0275\u0276\u0007\u0014\u0000"+ + "\u0000\u0276\u0277\u0007\r\u0000\u0000\u0277\u0278\u0007\r\u0000\u0000"+ + "\u0278\u0279\u0001\u0000\u0000\u0000\u0279\u027a\u0006\u0014\n\u0000\u027a"+ + "9\u0001\u0000\u0000\u0000\u027b\u027c\u0004\u0015\u0005\u0000\u027c\u027d"+ + "\u0007\r\u0000\u0000\u027d\u027e\u0007\u0003\u0000\u0000\u027e\u027f\u0007"+ + "\u000f\u0000\u0000\u027f\u0280\u0007\u0005\u0000\u0000\u0280\u0281\u0001"+ + "\u0000\u0000\u0000\u0281\u0282\u0006\u0015\n\u0000\u0282;\u0001\u0000"+ + "\u0000\u0000\u0283\u0284\u0004\u0016\u0006\u0000\u0284\u0285\u0007\u0006"+ + "\u0000\u0000\u0285\u0286\u0007\u0001\u0000\u0000\u0286\u0287\u0007\u0011"+ + "\u0000\u0000\u0287\u0288\u0007\n\u0000\u0000\u0288\u0289\u0007\u0005\u0000"+ + "\u0000\u0289\u028a\u0001\u0000\u0000\u0000\u028a\u028b\u0006\u0016\n\u0000"+ + "\u028b=\u0001\u0000\u0000\u0000\u028c\u028d\u0004\u0017\u0007\u0000\u028d"+ + "\u028e\u0007\r\u0000\u0000\u028e\u028f\u0007\u0007\u0000\u0000\u028f\u0290"+ + "\u0007\u0007\u0000\u0000\u0290\u0291\u0007\u0012\u0000\u0000\u0291\u0292"+ + "\u0007\u0014\u0000\u0000\u0292\u0293\u0007\b\u0000\u0000\u0293\u0294\u0001"+ + "\u0000\u0000\u0000\u0294\u0295\u0006\u0017\n\u0000\u0295?\u0001\u0000"+ + "\u0000\u0000\u0296\u0298\b\u0016\u0000\u0000\u0297\u0296\u0001\u0000\u0000"+ + "\u0000\u0298\u0299\u0001\u0000\u0000\u0000\u0299\u0297\u0001\u0000\u0000"+ + "\u0000\u0299\u029a\u0001\u0000\u0000\u0000\u029a\u029b\u0001\u0000\u0000"+ + "\u0000\u029b\u029c\u0006\u0018\u0000\u0000\u029cA\u0001\u0000\u0000\u0000"+ + "\u029d\u029e\u0005/\u0000\u0000\u029e\u029f\u0005/\u0000\u0000\u029f\u02a3"+ + "\u0001\u0000\u0000\u0000\u02a0\u02a2\b\u0017\u0000\u0000\u02a1\u02a0\u0001"+ + "\u0000\u0000\u0000\u02a2\u02a5\u0001\u0000\u0000\u0000\u02a3\u02a1\u0001"+ + "\u0000\u0000\u0000\u02a3\u02a4\u0001\u0000\u0000\u0000\u02a4\u02a7\u0001"+ + "\u0000\u0000\u0000\u02a5\u02a3\u0001\u0000\u0000\u0000\u02a6\u02a8\u0005"+ + "\r\u0000\u0000\u02a7\u02a6\u0001\u0000\u0000\u0000\u02a7\u02a8\u0001\u0000"+ + "\u0000\u0000\u02a8\u02aa\u0001\u0000\u0000\u0000\u02a9\u02ab\u0005\n\u0000"+ + "\u0000\u02aa\u02a9\u0001\u0000\u0000\u0000\u02aa\u02ab\u0001\u0000\u0000"+ + "\u0000\u02ab\u02ac\u0001\u0000\u0000\u0000\u02ac\u02ad\u0006\u0019\u000b"+ + "\u0000\u02adC\u0001\u0000\u0000\u0000\u02ae\u02af\u0005/\u0000\u0000\u02af"+ + "\u02b0\u0005*\u0000\u0000\u02b0\u02b5\u0001\u0000\u0000\u0000\u02b1\u02b4"+ + "\u0003D\u001a\u0000\u02b2\u02b4\t\u0000\u0000\u0000\u02b3\u02b1\u0001"+ + "\u0000\u0000\u0000\u02b3\u02b2\u0001\u0000\u0000\u0000\u02b4\u02b7\u0001"+ + "\u0000\u0000\u0000\u02b5\u02b6\u0001\u0000\u0000\u0000\u02b5\u02b3\u0001"+ + "\u0000\u0000\u0000\u02b6\u02b8\u0001\u0000\u0000\u0000\u02b7\u02b5\u0001"+ + "\u0000\u0000\u0000\u02b8\u02b9\u0005*\u0000\u0000\u02b9\u02ba\u0005/\u0000"+ + "\u0000\u02ba\u02bb\u0001\u0000\u0000\u0000\u02bb\u02bc\u0006\u001a\u000b"+ + "\u0000\u02bcE\u0001\u0000\u0000\u0000\u02bd\u02bf\u0007\u0018\u0000\u0000"+ + "\u02be\u02bd\u0001\u0000\u0000\u0000\u02bf\u02c0\u0001\u0000\u0000\u0000"+ + "\u02c0\u02be\u0001\u0000\u0000\u0000\u02c0\u02c1\u0001\u0000\u0000\u0000"+ + "\u02c1\u02c2\u0001\u0000\u0000\u0000\u02c2\u02c3\u0006\u001b\u000b\u0000"+ + "\u02c3G\u0001\u0000\u0000\u0000\u02c4\u02c5\u0005|\u0000\u0000\u02c5\u02c6"+ + "\u0001\u0000\u0000\u0000\u02c6\u02c7\u0006\u001c\f\u0000\u02c7I\u0001"+ + "\u0000\u0000\u0000\u02c8\u02c9\u0007\u0019\u0000\u0000\u02c9K\u0001\u0000"+ + "\u0000\u0000\u02ca\u02cb\u0007\u001a\u0000\u0000\u02cbM\u0001\u0000\u0000"+ + "\u0000\u02cc\u02cd\u0005\\\u0000\u0000\u02cd\u02ce\u0007\u001b\u0000\u0000"+ + "\u02ceO\u0001\u0000\u0000\u0000\u02cf\u02d0\b\u001c\u0000\u0000\u02d0"+ + "Q\u0001\u0000\u0000\u0000\u02d1\u02d3\u0007\u0003\u0000\u0000\u02d2\u02d4"+ + "\u0007\u001d\u0000\u0000\u02d3\u02d2\u0001\u0000\u0000\u0000\u02d3\u02d4"+ + "\u0001\u0000\u0000\u0000\u02d4\u02d6\u0001\u0000\u0000\u0000\u02d5\u02d7"+ + "\u0003J\u001d\u0000\u02d6\u02d5\u0001\u0000\u0000\u0000\u02d7\u02d8\u0001"+ + "\u0000\u0000\u0000\u02d8\u02d6\u0001\u0000\u0000\u0000\u02d8\u02d9\u0001"+ + "\u0000\u0000\u0000\u02d9S\u0001\u0000\u0000\u0000\u02da\u02db\u0005@\u0000"+ + "\u0000\u02dbU\u0001\u0000\u0000\u0000\u02dc\u02dd\u0005`\u0000\u0000\u02dd"+ + "W\u0001\u0000\u0000\u0000\u02de\u02e2\b\u001e\u0000\u0000\u02df\u02e0"+ + "\u0005`\u0000\u0000\u02e0\u02e2\u0005`\u0000\u0000\u02e1\u02de\u0001\u0000"+ + "\u0000\u0000\u02e1\u02df\u0001\u0000\u0000\u0000\u02e2Y\u0001\u0000\u0000"+ + "\u0000\u02e3\u02e4\u0005_\u0000\u0000\u02e4[\u0001\u0000\u0000\u0000\u02e5"+ + "\u02e9\u0003L\u001e\u0000\u02e6\u02e9\u0003J\u001d\u0000\u02e7\u02e9\u0003"+ + "Z%\u0000\u02e8\u02e5\u0001\u0000\u0000\u0000\u02e8\u02e6\u0001\u0000\u0000"+ + "\u0000\u02e8\u02e7\u0001\u0000\u0000\u0000\u02e9]\u0001\u0000\u0000\u0000"+ + "\u02ea\u02ef\u0005\"\u0000\u0000\u02eb\u02ee\u0003N\u001f\u0000\u02ec"+ + "\u02ee\u0003P \u0000\u02ed\u02eb\u0001\u0000\u0000\u0000\u02ed\u02ec\u0001"+ + "\u0000\u0000\u0000\u02ee\u02f1\u0001\u0000\u0000\u0000\u02ef\u02ed\u0001"+ + "\u0000\u0000\u0000\u02ef\u02f0\u0001\u0000\u0000\u0000\u02f0\u02f2\u0001"+ + "\u0000\u0000\u0000\u02f1\u02ef\u0001\u0000\u0000\u0000\u02f2\u0308\u0005"+ + "\"\u0000\u0000\u02f3\u02f4\u0005\"\u0000\u0000\u02f4\u02f5\u0005\"\u0000"+ + "\u0000\u02f5\u02f6\u0005\"\u0000\u0000\u02f6\u02fa\u0001\u0000\u0000\u0000"+ + "\u02f7\u02f9\b\u0017\u0000\u0000\u02f8\u02f7\u0001\u0000\u0000\u0000\u02f9"+ + "\u02fc\u0001\u0000\u0000\u0000\u02fa\u02fb\u0001\u0000\u0000\u0000\u02fa"+ + "\u02f8\u0001\u0000\u0000\u0000\u02fb\u02fd\u0001\u0000\u0000\u0000\u02fc"+ + "\u02fa\u0001\u0000\u0000\u0000\u02fd\u02fe\u0005\"\u0000\u0000\u02fe\u02ff"+ + "\u0005\"\u0000\u0000\u02ff\u0300\u0005\"\u0000\u0000\u0300\u0302\u0001"+ + "\u0000\u0000\u0000\u0301\u0303\u0005\"\u0000\u0000\u0302\u0301\u0001\u0000"+ + "\u0000\u0000\u0302\u0303\u0001\u0000\u0000\u0000\u0303\u0305\u0001\u0000"+ + "\u0000\u0000\u0304\u0306\u0005\"\u0000\u0000\u0305\u0304\u0001\u0000\u0000"+ + "\u0000\u0305\u0306\u0001\u0000\u0000\u0000\u0306\u0308\u0001\u0000\u0000"+ + "\u0000\u0307\u02ea\u0001\u0000\u0000\u0000\u0307\u02f3\u0001\u0000\u0000"+ + "\u0000\u0308_\u0001\u0000\u0000\u0000\u0309\u030b\u0003J\u001d\u0000\u030a"+ + "\u0309\u0001\u0000\u0000\u0000\u030b\u030c\u0001\u0000\u0000\u0000\u030c"+ + "\u030a\u0001\u0000\u0000\u0000\u030c\u030d\u0001\u0000\u0000\u0000\u030d"+ + "a\u0001\u0000\u0000\u0000\u030e\u0310\u0003J\u001d\u0000\u030f\u030e\u0001"+ + "\u0000\u0000\u0000\u0310\u0311\u0001\u0000\u0000\u0000\u0311\u030f\u0001"+ + "\u0000\u0000\u0000\u0311\u0312\u0001\u0000\u0000\u0000\u0312\u0313\u0001"+ + "\u0000\u0000\u0000\u0313\u0317\u0003t2\u0000\u0314\u0316\u0003J\u001d"+ + "\u0000\u0315\u0314\u0001\u0000\u0000\u0000\u0316\u0319\u0001\u0000\u0000"+ + "\u0000\u0317\u0315\u0001\u0000\u0000\u0000\u0317\u0318\u0001\u0000\u0000"+ + "\u0000\u0318\u0339\u0001\u0000\u0000\u0000\u0319\u0317\u0001\u0000\u0000"+ + "\u0000\u031a\u031c\u0003t2\u0000\u031b\u031d\u0003J\u001d\u0000\u031c"+ + "\u031b\u0001\u0000\u0000\u0000\u031d\u031e\u0001\u0000\u0000\u0000\u031e"+ + "\u031c\u0001\u0000\u0000\u0000\u031e\u031f\u0001\u0000\u0000\u0000\u031f"+ + "\u0339\u0001\u0000\u0000\u0000\u0320\u0322\u0003J\u001d\u0000\u0321\u0320"+ + "\u0001\u0000\u0000\u0000\u0322\u0323\u0001\u0000\u0000\u0000\u0323\u0321"+ + "\u0001\u0000\u0000\u0000\u0323\u0324\u0001\u0000\u0000\u0000\u0324\u032c"+ + "\u0001\u0000\u0000\u0000\u0325\u0329\u0003t2\u0000\u0326\u0328\u0003J"+ + "\u001d\u0000\u0327\u0326\u0001\u0000\u0000\u0000\u0328\u032b\u0001\u0000"+ + "\u0000\u0000\u0329\u0327\u0001\u0000\u0000\u0000\u0329\u032a\u0001\u0000"+ + "\u0000\u0000\u032a\u032d\u0001\u0000\u0000\u0000\u032b\u0329\u0001\u0000"+ + "\u0000\u0000\u032c\u0325\u0001\u0000\u0000\u0000\u032c\u032d\u0001\u0000"+ + "\u0000\u0000\u032d\u032e\u0001\u0000\u0000\u0000\u032e\u032f\u0003R!\u0000"+ + "\u032f\u0339\u0001\u0000\u0000\u0000\u0330\u0332\u0003t2\u0000\u0331\u0333"+ + "\u0003J\u001d\u0000\u0332\u0331\u0001\u0000\u0000\u0000\u0333\u0334\u0001"+ + "\u0000\u0000\u0000\u0334\u0332\u0001\u0000\u0000\u0000\u0334\u0335\u0001"+ + "\u0000\u0000\u0000\u0335\u0336\u0001\u0000\u0000\u0000\u0336\u0337\u0003"+ + "R!\u0000\u0337\u0339\u0001\u0000\u0000\u0000\u0338\u030f\u0001\u0000\u0000"+ + "\u0000\u0338\u031a\u0001\u0000\u0000\u0000\u0338\u0321\u0001\u0000\u0000"+ + "\u0000\u0338\u0330\u0001\u0000\u0000\u0000\u0339c\u0001\u0000\u0000\u0000"+ + "\u033a\u033b\u0007\u001f\u0000\u0000\u033b\u033c\u0007 \u0000\u0000\u033c"+ + "e\u0001\u0000\u0000\u0000\u033d\u033e\u0007\f\u0000\u0000\u033e\u033f"+ + "\u0007\t\u0000\u0000\u033f\u0340\u0007\u0000\u0000\u0000\u0340g\u0001"+ + "\u0000\u0000\u0000\u0341\u0342\u0007\f\u0000\u0000\u0342\u0343\u0007\u0002"+ + "\u0000\u0000\u0343\u0344\u0007\u0004\u0000\u0000\u0344i\u0001\u0000\u0000"+ + "\u0000\u0345\u0346\u0005=\u0000\u0000\u0346k\u0001\u0000\u0000\u0000\u0347"+ + "\u0348\u0005:\u0000\u0000\u0348\u0349\u0005:\u0000\u0000\u0349m\u0001"+ + "\u0000\u0000\u0000\u034a\u034b\u0005:\u0000\u0000\u034bo\u0001\u0000\u0000"+ + "\u0000\u034c\u034d\u0005,\u0000\u0000\u034dq\u0001\u0000\u0000\u0000\u034e"+ + "\u034f\u0007\u0000\u0000\u0000\u034f\u0350\u0007\u0003\u0000\u0000\u0350"+ + "\u0351\u0007\u0002\u0000\u0000\u0351\u0352\u0007\u0004\u0000\u0000\u0352"+ + "s\u0001\u0000\u0000\u0000\u0353\u0354\u0005.\u0000\u0000\u0354u\u0001"+ + "\u0000\u0000\u0000\u0355\u0356\u0007\u000f\u0000\u0000\u0356\u0357\u0007"+ + "\f\u0000\u0000\u0357\u0358\u0007\r\u0000\u0000\u0358\u0359\u0007\u0002"+ + "\u0000\u0000\u0359\u035a\u0007\u0003\u0000\u0000\u035aw\u0001\u0000\u0000"+ + "\u0000\u035b\u035c\u0007\u000f\u0000\u0000\u035c\u035d\u0007\u0001\u0000"+ + "\u0000\u035d\u035e\u0007\u0006\u0000\u0000\u035e\u035f\u0007\u0002\u0000"+ + "\u0000\u035f\u0360\u0007\u0005\u0000\u0000\u0360y\u0001\u0000\u0000\u0000"+ + "\u0361\u0362\u0007\u0001\u0000\u0000\u0362\u0363\u0007\t\u0000\u0000\u0363"+ + "{\u0001\u0000\u0000\u0000\u0364\u0365\u0007\u0001\u0000\u0000\u0365\u0366"+ + "\u0007\u0002\u0000\u0000\u0366}\u0001\u0000\u0000\u0000\u0367\u0368\u0007"+ + "\r\u0000\u0000\u0368\u0369\u0007\f\u0000\u0000\u0369\u036a\u0007\u0002"+ + "\u0000\u0000\u036a\u036b\u0007\u0005\u0000\u0000\u036b\u007f\u0001\u0000"+ + "\u0000\u0000\u036c\u036d\u0007\r\u0000\u0000\u036d\u036e\u0007\u0001\u0000"+ + "\u0000\u036e\u036f\u0007\u0012\u0000\u0000\u036f\u0370\u0007\u0003\u0000"+ + "\u0000\u0370\u0081\u0001\u0000\u0000\u0000\u0371\u0372\u0005(\u0000\u0000"+ + "\u0372\u0083\u0001\u0000\u0000\u0000\u0373\u0374\u0007\t\u0000\u0000\u0374"+ + "\u0375\u0007\u0007\u0000\u0000\u0375\u0376\u0007\u0005\u0000\u0000\u0376"+ + "\u0085\u0001\u0000\u0000\u0000\u0377\u0378\u0007\t\u0000\u0000\u0378\u0379"+ + "\u0007\u0014\u0000\u0000\u0379\u037a\u0007\r\u0000\u0000\u037a\u037b\u0007"+ + "\r\u0000\u0000\u037b\u0087\u0001\u0000\u0000\u0000\u037c\u037d\u0007\t"+ + "\u0000\u0000\u037d\u037e\u0007\u0014\u0000\u0000\u037e\u037f\u0007\r\u0000"+ + "\u0000\u037f\u0380\u0007\r\u0000\u0000\u0380\u0381\u0007\u0002\u0000\u0000"+ + "\u0381\u0089\u0001\u0000\u0000\u0000\u0382\u0383\u0007\u0007\u0000\u0000"+ + "\u0383\u0384\u0007\u0006\u0000\u0000\u0384\u008b\u0001\u0000\u0000\u0000"+ + "\u0385\u0386\u0005?\u0000\u0000\u0386\u008d\u0001\u0000\u0000\u0000\u0387"+ + "\u0388\u0007\u0006\u0000\u0000\u0388\u0389\u0007\r\u0000\u0000\u0389\u038a"+ + "\u0007\u0001\u0000\u0000\u038a\u038b\u0007\u0012\u0000\u0000\u038b\u038c"+ + "\u0007\u0003\u0000\u0000\u038c\u008f\u0001\u0000\u0000\u0000\u038d\u038e"+ + "\u0005)\u0000\u0000\u038e\u0091\u0001\u0000\u0000\u0000\u038f\u0390\u0007"+ + "\u0005\u0000\u0000\u0390\u0391\u0007\u0006\u0000\u0000\u0391\u0392\u0007"+ + "\u0014\u0000\u0000\u0392\u0393\u0007\u0003\u0000\u0000\u0393\u0093\u0001"+ + "\u0000\u0000\u0000\u0394\u0395\u0005=\u0000\u0000\u0395\u0396\u0005=\u0000"+ + "\u0000\u0396\u0095\u0001\u0000\u0000\u0000\u0397\u0398\u0005=\u0000\u0000"+ + "\u0398\u0399\u0005~\u0000\u0000\u0399\u0097\u0001\u0000\u0000\u0000\u039a"+ + "\u039b\u0005!\u0000\u0000\u039b\u039c\u0005=\u0000\u0000\u039c\u0099\u0001"+ + "\u0000\u0000\u0000\u039d\u039e\u0005<\u0000\u0000\u039e\u009b\u0001\u0000"+ + "\u0000\u0000\u039f\u03a0\u0005<\u0000\u0000\u03a0\u03a1\u0005=\u0000\u0000"+ + "\u03a1\u009d\u0001\u0000\u0000\u0000\u03a2\u03a3\u0005>\u0000\u0000\u03a3"+ + "\u009f\u0001\u0000\u0000\u0000\u03a4\u03a5\u0005>\u0000\u0000\u03a5\u03a6"+ + "\u0005=\u0000\u0000\u03a6\u00a1\u0001\u0000\u0000\u0000\u03a7\u03a8\u0005"+ + "+\u0000\u0000\u03a8\u00a3\u0001\u0000\u0000\u0000\u03a9\u03aa\u0005-\u0000"+ + "\u0000\u03aa\u00a5\u0001\u0000\u0000\u0000\u03ab\u03ac\u0005*\u0000\u0000"+ + "\u03ac\u00a7\u0001\u0000\u0000\u0000\u03ad\u03ae\u0005/\u0000\u0000\u03ae"+ + "\u00a9\u0001\u0000\u0000\u0000\u03af\u03b0\u0005%\u0000\u0000\u03b0\u00ab"+ + "\u0001\u0000\u0000\u0000\u03b1\u03b2\u0004N\b\u0000\u03b2\u03b3\u0005"+ + "{\u0000\u0000\u03b3\u00ad\u0001\u0000\u0000\u0000\u03b4\u03b5\u0004O\t"+ + "\u0000\u03b5\u03b6\u0005}\u0000\u0000\u03b6\u00af\u0001\u0000\u0000\u0000"+ + "\u03b7\u03b8\u0003.\u000f\u0000\u03b8\u03b9\u0001\u0000\u0000\u0000\u03b9"+ + "\u03ba\u0006P\r\u0000\u03ba\u00b1\u0001\u0000\u0000\u0000\u03bb\u03be"+ + "\u0003\u008c>\u0000\u03bc\u03bf\u0003L\u001e\u0000\u03bd\u03bf\u0003Z"+ + "%\u0000\u03be\u03bc\u0001\u0000\u0000\u0000\u03be\u03bd\u0001\u0000\u0000"+ + "\u0000\u03bf\u03c3\u0001\u0000\u0000\u0000\u03c0\u03c2\u0003\\&\u0000"+ + "\u03c1\u03c0\u0001\u0000\u0000\u0000\u03c2\u03c5\u0001\u0000\u0000\u0000"+ + "\u03c3\u03c1\u0001\u0000\u0000\u0000\u03c3\u03c4\u0001\u0000\u0000\u0000"+ + "\u03c4\u03cd\u0001\u0000\u0000\u0000\u03c5\u03c3\u0001\u0000\u0000\u0000"+ + "\u03c6\u03c8\u0003\u008c>\u0000\u03c7\u03c9\u0003J\u001d\u0000\u03c8\u03c7"+ + "\u0001\u0000\u0000\u0000\u03c9\u03ca\u0001\u0000\u0000\u0000\u03ca\u03c8"+ + "\u0001\u0000\u0000\u0000\u03ca\u03cb\u0001\u0000\u0000\u0000\u03cb\u03cd"+ + "\u0001\u0000\u0000\u0000\u03cc\u03bb\u0001\u0000\u0000\u0000\u03cc\u03c6"+ + "\u0001\u0000\u0000\u0000\u03cd\u00b3\u0001\u0000\u0000\u0000\u03ce\u03cf"+ + "\u0005[\u0000\u0000\u03cf\u03d0\u0001\u0000\u0000\u0000\u03d0\u03d1\u0006"+ + "R\u0000\u0000\u03d1\u03d2\u0006R\u0000\u0000\u03d2\u00b5\u0001\u0000\u0000"+ + "\u0000\u03d3\u03d4\u0005]\u0000\u0000\u03d4\u03d5\u0001\u0000\u0000\u0000"+ + "\u03d5\u03d6\u0006S\f\u0000\u03d6\u03d7\u0006S\f\u0000\u03d7\u00b7\u0001"+ + "\u0000\u0000\u0000\u03d8\u03dc\u0003L\u001e\u0000\u03d9\u03db\u0003\\"+ + "&\u0000\u03da\u03d9\u0001\u0000\u0000\u0000\u03db\u03de\u0001\u0000\u0000"+ + "\u0000\u03dc\u03da\u0001\u0000\u0000\u0000\u03dc\u03dd\u0001\u0000\u0000"+ + "\u0000\u03dd\u03e9\u0001\u0000\u0000\u0000\u03de\u03dc\u0001\u0000\u0000"+ + "\u0000\u03df\u03e2\u0003Z%\u0000\u03e0\u03e2\u0003T\"\u0000\u03e1\u03df"+ + "\u0001\u0000\u0000\u0000\u03e1\u03e0\u0001\u0000\u0000\u0000\u03e2\u03e4"+ + "\u0001\u0000\u0000\u0000\u03e3\u03e5\u0003\\&\u0000\u03e4\u03e3\u0001"+ + "\u0000\u0000\u0000\u03e5\u03e6\u0001\u0000\u0000\u0000\u03e6\u03e4\u0001"+ + "\u0000\u0000\u0000\u03e6\u03e7\u0001\u0000\u0000\u0000\u03e7\u03e9\u0001"+ + "\u0000\u0000\u0000\u03e8\u03d8\u0001\u0000\u0000\u0000\u03e8\u03e1\u0001"+ + "\u0000\u0000\u0000\u03e9\u00b9\u0001\u0000\u0000\u0000\u03ea\u03ec\u0003"+ + "V#\u0000\u03eb\u03ed\u0003X$\u0000\u03ec\u03eb\u0001\u0000\u0000\u0000"+ + "\u03ed\u03ee\u0001\u0000\u0000\u0000\u03ee\u03ec\u0001\u0000\u0000\u0000"+ + "\u03ee\u03ef\u0001\u0000\u0000\u0000\u03ef\u03f0\u0001\u0000\u0000\u0000"+ + "\u03f0\u03f1\u0003V#\u0000\u03f1\u00bb\u0001\u0000\u0000\u0000\u03f2\u03f3"+ + "\u0003\u00baU\u0000\u03f3\u00bd\u0001\u0000\u0000\u0000\u03f4\u03f5\u0003"+ + "B\u0019\u0000\u03f5\u03f6\u0001\u0000\u0000\u0000\u03f6\u03f7\u0006W\u000b"+ + "\u0000\u03f7\u00bf\u0001\u0000\u0000\u0000\u03f8\u03f9\u0003D\u001a\u0000"+ + "\u03f9\u03fa\u0001\u0000\u0000\u0000\u03fa\u03fb\u0006X\u000b\u0000\u03fb"+ + "\u00c1\u0001\u0000\u0000\u0000\u03fc\u03fd\u0003F\u001b\u0000\u03fd\u03fe"+ + "\u0001\u0000\u0000\u0000\u03fe\u03ff\u0006Y\u000b\u0000\u03ff\u00c3\u0001"+ + "\u0000\u0000\u0000\u0400\u0401\u0003\u00b4R\u0000\u0401\u0402\u0001\u0000"+ + "\u0000\u0000\u0402\u0403\u0006Z\u000e\u0000\u0403\u0404\u0006Z\u000f\u0000"+ + "\u0404\u00c5\u0001\u0000\u0000\u0000\u0405\u0406\u0003H\u001c\u0000\u0406"+ + "\u0407\u0001\u0000\u0000\u0000\u0407\u0408\u0006[\u0010\u0000\u0408\u0409"+ + "\u0006[\f\u0000\u0409\u00c7\u0001\u0000\u0000\u0000\u040a\u040b\u0003"+ + "F\u001b\u0000\u040b\u040c\u0001\u0000\u0000\u0000\u040c\u040d\u0006\\"+ + "\u000b\u0000\u040d\u00c9\u0001\u0000\u0000\u0000\u040e\u040f\u0003B\u0019"+ + "\u0000\u040f\u0410\u0001\u0000\u0000\u0000\u0410\u0411\u0006]\u000b\u0000"+ + "\u0411\u00cb\u0001\u0000\u0000\u0000\u0412\u0413\u0003D\u001a\u0000\u0413"+ + "\u0414\u0001\u0000\u0000\u0000\u0414\u0415\u0006^\u000b\u0000\u0415\u00cd"+ + "\u0001\u0000\u0000\u0000\u0416\u0417\u0003H\u001c\u0000\u0417\u0418\u0001"+ + "\u0000\u0000\u0000\u0418\u0419\u0006_\u0010\u0000\u0419\u041a\u0006_\f"+ + "\u0000\u041a\u00cf\u0001\u0000\u0000\u0000\u041b\u041c\u0003\u00b4R\u0000"+ + "\u041c\u041d\u0001\u0000\u0000\u0000\u041d\u041e\u0006`\u000e\u0000\u041e"+ + "\u00d1\u0001\u0000\u0000\u0000\u041f\u0420\u0003\u00b6S\u0000\u0420\u0421"+ + "\u0001\u0000\u0000\u0000\u0421\u0422\u0006a\u0011\u0000\u0422\u00d3\u0001"+ + "\u0000\u0000\u0000\u0423\u0424\u0003n/\u0000\u0424\u0425\u0001\u0000\u0000"+ + "\u0000\u0425\u0426\u0006b\u0012\u0000\u0426\u00d5\u0001\u0000\u0000\u0000"+ + "\u0427\u0428\u0003p0\u0000\u0428\u0429\u0001\u0000\u0000\u0000\u0429\u042a"+ + "\u0006c\u0013\u0000\u042a\u00d7\u0001\u0000\u0000\u0000\u042b\u042c\u0003"+ + "j-\u0000\u042c\u042d\u0001\u0000\u0000\u0000\u042d\u042e\u0006d\u0014"+ + "\u0000\u042e\u00d9\u0001\u0000\u0000\u0000\u042f\u0430\u0007\u0010\u0000"+ + "\u0000\u0430\u0431\u0007\u0003\u0000\u0000\u0431\u0432\u0007\u0005\u0000"+ + "\u0000\u0432\u0433\u0007\f\u0000\u0000\u0433\u0434\u0007\u0000\u0000\u0000"+ + "\u0434\u0435\u0007\f\u0000\u0000\u0435\u0436\u0007\u0005\u0000\u0000\u0436"+ + "\u0437\u0007\f\u0000\u0000\u0437\u00db\u0001\u0000\u0000\u0000\u0438\u043c"+ + "\b!\u0000\u0000\u0439\u043a\u0005/\u0000\u0000\u043a\u043c\b\"\u0000\u0000"+ + "\u043b\u0438\u0001\u0000\u0000\u0000\u043b\u0439\u0001\u0000\u0000\u0000"+ + "\u043c\u00dd\u0001\u0000\u0000\u0000\u043d\u043f\u0003\u00dcf\u0000\u043e"+ + "\u043d\u0001\u0000\u0000\u0000\u043f\u0440\u0001\u0000\u0000\u0000\u0440"+ + "\u043e\u0001\u0000\u0000\u0000\u0440\u0441\u0001\u0000\u0000\u0000\u0441"+ + "\u00df\u0001\u0000\u0000\u0000\u0442\u0443\u0003\u00deg\u0000\u0443\u0444"+ + "\u0001\u0000\u0000\u0000\u0444\u0445\u0006h\u0015\u0000\u0445\u00e1\u0001"+ + "\u0000\u0000\u0000\u0446\u0447\u0003^\'\u0000\u0447\u0448\u0001\u0000"+ + "\u0000\u0000\u0448\u0449\u0006i\u0016\u0000\u0449\u00e3\u0001\u0000\u0000"+ + "\u0000\u044a\u044b\u0003B\u0019\u0000\u044b\u044c\u0001\u0000\u0000\u0000"+ + "\u044c\u044d\u0006j\u000b\u0000\u044d\u00e5\u0001\u0000\u0000\u0000\u044e"+ + "\u044f\u0003D\u001a\u0000\u044f\u0450\u0001\u0000\u0000\u0000\u0450\u0451"+ + "\u0006k\u000b\u0000\u0451\u00e7\u0001\u0000\u0000\u0000\u0452\u0453\u0003"+ + "F\u001b\u0000\u0453\u0454\u0001\u0000\u0000\u0000\u0454\u0455\u0006l\u000b"+ + "\u0000\u0455\u00e9\u0001\u0000\u0000\u0000\u0456\u0457\u0003H\u001c\u0000"+ + "\u0457\u0458\u0001\u0000\u0000\u0000\u0458\u0459\u0006m\u0010\u0000\u0459"+ + "\u045a\u0006m\f\u0000\u045a\u00eb\u0001\u0000\u0000\u0000\u045b\u045c"+ + "\u0003t2\u0000\u045c\u045d\u0001\u0000\u0000\u0000\u045d\u045e\u0006n"+ + "\u0017\u0000\u045e\u00ed\u0001\u0000\u0000\u0000\u045f\u0460\u0003p0\u0000"+ + "\u0460\u0461\u0001\u0000\u0000\u0000\u0461\u0462\u0006o\u0013\u0000\u0462"+ + "\u00ef\u0001\u0000\u0000\u0000\u0463\u0464\u0004p\n\u0000\u0464\u0465"+ + "\u0003\u008c>\u0000\u0465\u0466\u0001\u0000\u0000\u0000\u0466\u0467\u0006"+ + "p\u0018\u0000\u0467\u00f1\u0001\u0000\u0000\u0000\u0468\u0469\u0004q\u000b"+ + "\u0000\u0469\u046a\u0003\u00b2Q\u0000\u046a\u046b\u0001\u0000\u0000\u0000"+ + "\u046b\u046c\u0006q\u0019\u0000\u046c\u00f3\u0001\u0000\u0000\u0000\u046d"+ + "\u0472\u0003L\u001e\u0000\u046e\u0472\u0003J\u001d\u0000\u046f\u0472\u0003"+ + "Z%\u0000\u0470\u0472\u0003\u00a6K\u0000\u0471\u046d\u0001\u0000\u0000"+ + "\u0000\u0471\u046e\u0001\u0000\u0000\u0000\u0471\u046f\u0001\u0000\u0000"+ + "\u0000\u0471\u0470\u0001\u0000\u0000\u0000\u0472\u00f5\u0001\u0000\u0000"+ + "\u0000\u0473\u0476\u0003L\u001e\u0000\u0474\u0476\u0003\u00a6K\u0000\u0475"+ + "\u0473\u0001\u0000\u0000\u0000\u0475\u0474\u0001\u0000\u0000\u0000\u0476"+ + "\u047a\u0001\u0000\u0000\u0000\u0477\u0479\u0003\u00f4r\u0000\u0478\u0477"+ + "\u0001\u0000\u0000\u0000\u0479\u047c\u0001\u0000\u0000\u0000\u047a\u0478"+ + "\u0001\u0000\u0000\u0000\u047a\u047b\u0001\u0000\u0000\u0000\u047b\u0487"+ + "\u0001\u0000\u0000\u0000\u047c\u047a\u0001\u0000\u0000\u0000\u047d\u0480"+ + "\u0003Z%\u0000\u047e\u0480\u0003T\"\u0000\u047f\u047d\u0001\u0000\u0000"+ + "\u0000\u047f\u047e\u0001\u0000\u0000\u0000\u0480\u0482\u0001\u0000\u0000"+ + "\u0000\u0481\u0483\u0003\u00f4r\u0000\u0482\u0481\u0001\u0000\u0000\u0000"+ + "\u0483\u0484\u0001\u0000\u0000\u0000\u0484\u0482\u0001\u0000\u0000\u0000"+ + "\u0484\u0485\u0001\u0000\u0000\u0000\u0485\u0487\u0001\u0000\u0000\u0000"+ + "\u0486\u0475\u0001\u0000\u0000\u0000\u0486\u047f\u0001\u0000\u0000\u0000"+ + "\u0487\u00f7\u0001\u0000\u0000\u0000\u0488\u048b\u0003\u00f6s\u0000\u0489"+ + "\u048b\u0003\u00baU\u0000\u048a\u0488\u0001\u0000\u0000\u0000\u048a\u0489"+ + "\u0001\u0000\u0000\u0000\u048b\u048c\u0001\u0000\u0000\u0000\u048c\u048a"+ + "\u0001\u0000\u0000\u0000\u048c\u048d\u0001\u0000\u0000\u0000\u048d\u00f9"+ + "\u0001\u0000\u0000\u0000\u048e\u048f\u0003B\u0019\u0000\u048f\u0490\u0001"+ + "\u0000\u0000\u0000\u0490\u0491\u0006u\u000b\u0000\u0491\u00fb\u0001\u0000"+ + "\u0000\u0000\u0492\u0493\u0003D\u001a\u0000\u0493\u0494\u0001\u0000\u0000"+ + "\u0000\u0494\u0495\u0006v\u000b\u0000\u0495\u00fd\u0001\u0000\u0000\u0000"+ + "\u0496\u0497\u0003F\u001b\u0000\u0497\u0498\u0001\u0000\u0000\u0000\u0498"+ + "\u0499\u0006w\u000b\u0000\u0499\u00ff\u0001\u0000\u0000\u0000\u049a\u049b"+ + "\u0003H\u001c\u0000\u049b\u049c\u0001\u0000\u0000\u0000\u049c\u049d\u0006"+ + "x\u0010\u0000\u049d\u049e\u0006x\f\u0000\u049e\u0101\u0001\u0000\u0000"+ + "\u0000\u049f\u04a0\u0003j-\u0000\u04a0\u04a1\u0001\u0000\u0000\u0000\u04a1"+ + "\u04a2\u0006y\u0014\u0000\u04a2\u0103\u0001\u0000\u0000\u0000\u04a3\u04a4"+ + "\u0003p0\u0000\u04a4\u04a5\u0001\u0000\u0000\u0000\u04a5\u04a6\u0006z"+ + "\u0013\u0000\u04a6\u0105\u0001\u0000\u0000\u0000\u04a7\u04a8\u0003t2\u0000"+ + "\u04a8\u04a9\u0001\u0000\u0000\u0000\u04a9\u04aa\u0006{\u0017\u0000\u04aa"+ + "\u0107\u0001\u0000\u0000\u0000\u04ab\u04ac\u0004|\f\u0000\u04ac\u04ad"+ + "\u0003\u008c>\u0000\u04ad\u04ae\u0001\u0000\u0000\u0000\u04ae\u04af\u0006"+ + "|\u0018\u0000\u04af\u0109\u0001\u0000\u0000\u0000\u04b0\u04b1\u0004}\r"+ + "\u0000\u04b1\u04b2\u0003\u00b2Q\u0000\u04b2\u04b3\u0001\u0000\u0000\u0000"+ + "\u04b3\u04b4\u0006}\u0019\u0000\u04b4\u010b\u0001\u0000\u0000\u0000\u04b5"+ + "\u04b6\u0007\f\u0000\u0000\u04b6\u04b7\u0007\u0002\u0000\u0000\u04b7\u010d"+ + "\u0001\u0000\u0000\u0000\u04b8\u04b9\u0003\u00f8t\u0000\u04b9\u04ba\u0001"+ + "\u0000\u0000\u0000\u04ba\u04bb\u0006\u007f\u001a\u0000\u04bb\u010f\u0001"+ + "\u0000\u0000\u0000\u04bc\u04bd\u0003B\u0019\u0000\u04bd\u04be\u0001\u0000"+ + "\u0000\u0000\u04be\u04bf\u0006\u0080\u000b\u0000\u04bf\u0111\u0001\u0000"+ + "\u0000\u0000\u04c0\u04c1\u0003D\u001a\u0000\u04c1\u04c2\u0001\u0000\u0000"+ + "\u0000\u04c2\u04c3\u0006\u0081\u000b\u0000\u04c3\u0113\u0001\u0000\u0000"+ + "\u0000\u04c4\u04c5\u0003F\u001b\u0000\u04c5\u04c6\u0001\u0000\u0000\u0000"+ + "\u04c6\u04c7\u0006\u0082\u000b\u0000\u04c7\u0115\u0001\u0000\u0000\u0000"+ + "\u04c8\u04c9\u0003H\u001c\u0000\u04c9\u04ca\u0001\u0000\u0000\u0000\u04ca"+ + "\u04cb\u0006\u0083\u0010\u0000\u04cb\u04cc\u0006\u0083\f\u0000\u04cc\u0117"+ + "\u0001\u0000\u0000\u0000\u04cd\u04ce\u0003\u00b4R\u0000\u04ce\u04cf\u0001"+ + "\u0000\u0000\u0000\u04cf\u04d0\u0006\u0084\u000e\u0000\u04d0\u04d1\u0006"+ + "\u0084\u001b\u0000\u04d1\u0119\u0001\u0000\u0000\u0000\u04d2\u04d3\u0007"+ + "\u0007\u0000\u0000\u04d3\u04d4\u0007\t\u0000\u0000\u04d4\u04d5\u0001\u0000"+ + "\u0000\u0000\u04d5\u04d6\u0006\u0085\u001c\u0000\u04d6\u011b\u0001\u0000"+ + "\u0000\u0000\u04d7\u04d8\u0007\u0013\u0000\u0000\u04d8\u04d9\u0007\u0001"+ + "\u0000\u0000\u04d9\u04da\u0007\u0005\u0000\u0000\u04da\u04db\u0007\n\u0000"+ + "\u0000\u04db\u04dc\u0001\u0000\u0000\u0000\u04dc\u04dd\u0006\u0086\u001c"+ + "\u0000\u04dd\u011d\u0001\u0000\u0000\u0000\u04de\u04df\b#\u0000\u0000"+ + "\u04df\u011f\u0001\u0000\u0000\u0000\u04e0\u04e2\u0003\u011e\u0087\u0000"+ + "\u04e1\u04e0\u0001\u0000\u0000\u0000\u04e2\u04e3\u0001\u0000\u0000\u0000"+ + "\u04e3\u04e1\u0001\u0000\u0000\u0000\u04e3\u04e4\u0001\u0000\u0000\u0000"+ + "\u04e4\u04e5\u0001\u0000\u0000\u0000\u04e5\u04e6\u0003n/\u0000\u04e6\u04e8"+ + "\u0001\u0000\u0000\u0000\u04e7\u04e1\u0001\u0000\u0000\u0000\u04e7\u04e8"+ + "\u0001\u0000\u0000\u0000\u04e8\u04ea\u0001\u0000\u0000\u0000\u04e9\u04eb"+ + "\u0003\u011e\u0087\u0000\u04ea\u04e9\u0001\u0000\u0000\u0000\u04eb\u04ec"+ + "\u0001\u0000\u0000\u0000\u04ec\u04ea\u0001\u0000\u0000\u0000\u04ec\u04ed"+ + "\u0001\u0000\u0000\u0000\u04ed\u0121\u0001\u0000\u0000\u0000\u04ee\u04ef"+ + "\u0003\u0120\u0088\u0000\u04ef\u04f0\u0001\u0000\u0000\u0000\u04f0\u04f1"+ + "\u0006\u0089\u001d\u0000\u04f1\u0123\u0001\u0000\u0000\u0000\u04f2\u04f3"+ + "\u0003B\u0019\u0000\u04f3\u04f4\u0001\u0000\u0000\u0000\u04f4\u04f5\u0006"+ + "\u008a\u000b\u0000\u04f5\u0125\u0001\u0000\u0000\u0000\u04f6\u04f7\u0003"+ + "D\u001a\u0000\u04f7\u04f8\u0001\u0000\u0000\u0000\u04f8\u04f9\u0006\u008b"+ + "\u000b\u0000\u04f9\u0127\u0001\u0000\u0000\u0000\u04fa\u04fb\u0003F\u001b"+ + "\u0000\u04fb\u04fc\u0001\u0000\u0000\u0000\u04fc\u04fd\u0006\u008c\u000b"+ + "\u0000\u04fd\u0129\u0001\u0000\u0000\u0000\u04fe\u04ff\u0003H\u001c\u0000"+ + "\u04ff\u0500\u0001\u0000\u0000\u0000\u0500\u0501\u0006\u008d\u0010\u0000"+ + "\u0501\u0502\u0006\u008d\f\u0000\u0502\u0503\u0006\u008d\f\u0000\u0503"+ + "\u012b\u0001\u0000\u0000\u0000\u0504\u0505\u0003j-\u0000\u0505\u0506\u0001"+ + "\u0000\u0000\u0000\u0506\u0507\u0006\u008e\u0014\u0000\u0507\u012d\u0001"+ + "\u0000\u0000\u0000\u0508\u0509\u0003p0\u0000\u0509\u050a\u0001\u0000\u0000"+ + "\u0000\u050a\u050b\u0006\u008f\u0013\u0000\u050b\u012f\u0001\u0000\u0000"+ + "\u0000\u050c\u050d\u0003t2\u0000\u050d\u050e\u0001\u0000\u0000\u0000\u050e"+ + "\u050f\u0006\u0090\u0017\u0000\u050f\u0131\u0001\u0000\u0000\u0000\u0510"+ + "\u0511\u0003\u011c\u0086\u0000\u0511\u0512\u0001\u0000\u0000\u0000\u0512"+ + "\u0513\u0006\u0091\u001e\u0000\u0513\u0133\u0001\u0000\u0000\u0000\u0514"+ + "\u0515\u0003\u00f8t\u0000\u0515\u0516\u0001\u0000\u0000\u0000\u0516\u0517"+ + "\u0006\u0092\u001a\u0000\u0517\u0135\u0001\u0000\u0000\u0000\u0518\u0519"+ + "\u0003\u00bcV\u0000\u0519\u051a\u0001\u0000\u0000\u0000\u051a\u051b\u0006"+ + "\u0093\u001f\u0000\u051b\u0137\u0001\u0000\u0000\u0000\u051c\u051d\u0004"+ + "\u0094\u000e\u0000\u051d\u051e\u0003\u008c>\u0000\u051e\u051f\u0001\u0000"+ + "\u0000\u0000\u051f\u0520\u0006\u0094\u0018\u0000\u0520\u0139\u0001\u0000"+ + "\u0000\u0000\u0521\u0522\u0004\u0095\u000f\u0000\u0522\u0523\u0003\u00b2"+ + "Q\u0000\u0523\u0524\u0001\u0000\u0000\u0000\u0524\u0525\u0006\u0095\u0019"+ + "\u0000\u0525\u013b\u0001\u0000\u0000\u0000\u0526\u0527\u0003B\u0019\u0000"+ + "\u0527\u0528\u0001\u0000\u0000\u0000\u0528\u0529\u0006\u0096\u000b\u0000"+ + "\u0529\u013d\u0001\u0000\u0000\u0000\u052a\u052b\u0003D\u001a\u0000\u052b"+ + "\u052c\u0001\u0000\u0000\u0000\u052c\u052d\u0006\u0097\u000b\u0000\u052d"+ + "\u013f\u0001\u0000\u0000\u0000\u052e\u052f\u0003F\u001b\u0000\u052f\u0530"+ + "\u0001\u0000\u0000\u0000\u0530\u0531\u0006\u0098\u000b\u0000\u0531\u0141"+ + "\u0001\u0000\u0000\u0000\u0532\u0533\u0003H\u001c\u0000\u0533\u0534\u0001"+ + "\u0000\u0000\u0000\u0534\u0535\u0006\u0099\u0010\u0000\u0535\u0536\u0006"+ + "\u0099\f\u0000\u0536\u0143\u0001\u0000\u0000\u0000\u0537\u0538\u0003t"+ + "2\u0000\u0538\u0539\u0001\u0000\u0000\u0000\u0539\u053a\u0006\u009a\u0017"+ + "\u0000\u053a\u0145\u0001\u0000\u0000\u0000\u053b\u053c\u0004\u009b\u0010"+ + "\u0000\u053c\u053d\u0003\u008c>\u0000\u053d\u053e\u0001\u0000\u0000\u0000"+ + "\u053e\u053f\u0006\u009b\u0018\u0000\u053f\u0147\u0001\u0000\u0000\u0000"+ + "\u0540\u0541\u0004\u009c\u0011\u0000\u0541\u0542\u0003\u00b2Q\u0000\u0542"+ + "\u0543\u0001\u0000\u0000\u0000\u0543\u0544\u0006\u009c\u0019\u0000\u0544"+ + "\u0149\u0001\u0000\u0000\u0000\u0545\u0546\u0003\u00bcV\u0000\u0546\u0547"+ + "\u0001\u0000\u0000\u0000\u0547\u0548\u0006\u009d\u001f\u0000\u0548\u014b"+ + "\u0001\u0000\u0000\u0000\u0549\u054a\u0003\u00b8T\u0000\u054a\u054b\u0001"+ + "\u0000\u0000\u0000\u054b\u054c\u0006\u009e \u0000\u054c\u014d\u0001\u0000"+ + "\u0000\u0000\u054d\u054e\u0003B\u0019\u0000\u054e\u054f\u0001\u0000\u0000"+ + "\u0000\u054f\u0550\u0006\u009f\u000b\u0000\u0550\u014f\u0001\u0000\u0000"+ + "\u0000\u0551\u0552\u0003D\u001a\u0000\u0552\u0553\u0001\u0000\u0000\u0000"+ + "\u0553\u0554\u0006\u00a0\u000b\u0000\u0554\u0151\u0001\u0000\u0000\u0000"+ + "\u0555\u0556\u0003F\u001b\u0000\u0556\u0557\u0001\u0000\u0000\u0000\u0557"+ + "\u0558\u0006\u00a1\u000b\u0000\u0558\u0153\u0001\u0000\u0000\u0000\u0559"+ + "\u055a\u0003H\u001c\u0000\u055a\u055b\u0001\u0000\u0000\u0000\u055b\u055c"+ + "\u0006\u00a2\u0010\u0000\u055c\u055d\u0006\u00a2\f\u0000\u055d\u0155\u0001"+ + "\u0000\u0000\u0000\u055e\u055f\u0007\u0001\u0000\u0000\u055f\u0560\u0007"+ + "\t\u0000\u0000\u0560\u0561\u0007\u000f\u0000\u0000\u0561\u0562\u0007\u0007"+ + "\u0000\u0000\u0562\u0157\u0001\u0000\u0000\u0000\u0563\u0564\u0003B\u0019"+ + "\u0000\u0564\u0565\u0001\u0000\u0000\u0000\u0565\u0566\u0006\u00a4\u000b"+ + "\u0000\u0566\u0159\u0001\u0000\u0000\u0000\u0567\u0568\u0003D\u001a\u0000"+ + "\u0568\u0569\u0001\u0000\u0000\u0000\u0569\u056a\u0006\u00a5\u000b\u0000"+ + "\u056a\u015b\u0001\u0000\u0000\u0000\u056b\u056c\u0003F\u001b\u0000\u056c"+ + "\u056d\u0001\u0000\u0000\u0000\u056d\u056e\u0006\u00a6\u000b\u0000\u056e"+ + "\u015d\u0001\u0000\u0000\u0000\u056f\u0570\u0003\u00b6S\u0000\u0570\u0571"+ + "\u0001\u0000\u0000\u0000\u0571\u0572\u0006\u00a7\u0011\u0000\u0572\u0573"+ + "\u0006\u00a7\f\u0000\u0573\u015f\u0001\u0000\u0000\u0000\u0574\u0575\u0003"+ + "n/\u0000\u0575\u0576\u0001\u0000\u0000\u0000\u0576\u0577\u0006\u00a8\u0012"+ + "\u0000\u0577\u0161\u0001\u0000\u0000\u0000\u0578\u057e\u0003T\"\u0000"+ + "\u0579\u057e\u0003J\u001d\u0000\u057a\u057e\u0003t2\u0000\u057b\u057e"+ + "\u0003L\u001e\u0000\u057c\u057e\u0003Z%\u0000\u057d\u0578\u0001\u0000"+ + "\u0000\u0000\u057d\u0579\u0001\u0000\u0000\u0000\u057d\u057a\u0001\u0000"+ + "\u0000\u0000\u057d\u057b\u0001\u0000\u0000\u0000\u057d\u057c\u0001\u0000"+ + "\u0000\u0000\u057e\u057f\u0001\u0000\u0000\u0000\u057f\u057d\u0001\u0000"+ + "\u0000\u0000\u057f\u0580\u0001\u0000\u0000\u0000\u0580\u0163\u0001\u0000"+ + "\u0000\u0000\u0581\u0582\u0003B\u0019\u0000\u0582\u0583\u0001\u0000\u0000"+ + "\u0000\u0583\u0584\u0006\u00aa\u000b\u0000\u0584\u0165\u0001\u0000\u0000"+ + "\u0000\u0585\u0586\u0003D\u001a\u0000\u0586\u0587\u0001\u0000\u0000\u0000"+ + "\u0587\u0588\u0006\u00ab\u000b\u0000\u0588\u0167\u0001\u0000\u0000\u0000"+ + "\u0589\u058a\u0003F\u001b\u0000\u058a\u058b\u0001\u0000\u0000\u0000\u058b"+ + "\u058c\u0006\u00ac\u000b\u0000\u058c\u0169\u0001\u0000\u0000\u0000\u058d"+ + "\u058e\u0003H\u001c\u0000\u058e\u058f\u0001\u0000\u0000\u0000\u058f\u0590"+ + "\u0006\u00ad\u0010\u0000\u0590\u0591\u0006\u00ad\f\u0000\u0591\u016b\u0001"+ + "\u0000\u0000\u0000\u0592\u0593\u0003n/\u0000\u0593\u0594\u0001\u0000\u0000"+ + "\u0000\u0594\u0595\u0006\u00ae\u0012\u0000\u0595\u016d\u0001\u0000\u0000"+ + "\u0000\u0596\u0597\u0003p0\u0000\u0597\u0598\u0001\u0000\u0000\u0000\u0598"+ + "\u0599\u0006\u00af\u0013\u0000\u0599\u016f\u0001\u0000\u0000\u0000\u059a"+ + "\u059b\u0003t2\u0000\u059b\u059c\u0001\u0000\u0000\u0000\u059c\u059d\u0006"+ + "\u00b0\u0017\u0000\u059d\u0171\u0001\u0000\u0000\u0000\u059e\u059f\u0003"+ + "\u011a\u0085\u0000\u059f\u05a0\u0001\u0000\u0000\u0000\u05a0\u05a1\u0006"+ + "\u00b1!\u0000\u05a1\u05a2\u0006\u00b1\"\u0000\u05a2\u0173\u0001\u0000"+ + "\u0000\u0000\u05a3\u05a4\u0003\u00deg\u0000\u05a4\u05a5\u0001\u0000\u0000"+ + "\u0000\u05a5\u05a6\u0006\u00b2\u0015\u0000\u05a6\u0175\u0001\u0000\u0000"+ + "\u0000\u05a7\u05a8\u0003^\'\u0000\u05a8\u05a9\u0001\u0000\u0000\u0000"+ + "\u05a9\u05aa\u0006\u00b3\u0016\u0000\u05aa\u0177\u0001\u0000\u0000\u0000"+ + "\u05ab\u05ac\u0003B\u0019\u0000\u05ac\u05ad\u0001\u0000\u0000\u0000\u05ad"+ + "\u05ae\u0006\u00b4\u000b\u0000\u05ae\u0179\u0001\u0000\u0000\u0000\u05af"+ + "\u05b0\u0003D\u001a\u0000\u05b0\u05b1\u0001\u0000\u0000\u0000\u05b1\u05b2"+ + "\u0006\u00b5\u000b\u0000\u05b2\u017b\u0001\u0000\u0000\u0000\u05b3\u05b4"+ + "\u0003F\u001b\u0000\u05b4\u05b5\u0001\u0000\u0000\u0000\u05b5\u05b6\u0006"+ + "\u00b6\u000b\u0000\u05b6\u017d\u0001\u0000\u0000\u0000\u05b7\u05b8\u0003"+ + "H\u001c\u0000\u05b8\u05b9\u0001\u0000\u0000\u0000\u05b9\u05ba\u0006\u00b7"+ + "\u0010\u0000\u05ba\u05bb\u0006\u00b7\f\u0000\u05bb\u05bc\u0006\u00b7\f"+ + "\u0000\u05bc\u017f\u0001\u0000\u0000\u0000\u05bd\u05be\u0003p0\u0000\u05be"+ + "\u05bf\u0001\u0000\u0000\u0000\u05bf\u05c0\u0006\u00b8\u0013\u0000\u05c0"+ + "\u0181\u0001\u0000\u0000\u0000\u05c1\u05c2\u0003t2\u0000\u05c2\u05c3\u0001"+ + "\u0000\u0000\u0000\u05c3\u05c4\u0006\u00b9\u0017\u0000\u05c4\u0183\u0001"+ + "\u0000\u0000\u0000\u05c5\u05c6\u0003\u00f8t\u0000\u05c6\u05c7\u0001\u0000"+ + "\u0000\u0000\u05c7\u05c8\u0006\u00ba\u001a\u0000\u05c8\u0185\u0001\u0000"+ + "\u0000\u0000\u05c9\u05ca\u0003B\u0019\u0000\u05ca\u05cb\u0001\u0000\u0000"+ + "\u0000\u05cb\u05cc\u0006\u00bb\u000b\u0000\u05cc\u0187\u0001\u0000\u0000"+ + "\u0000\u05cd\u05ce\u0003D\u001a\u0000\u05ce\u05cf\u0001\u0000\u0000\u0000"+ + "\u05cf\u05d0\u0006\u00bc\u000b\u0000\u05d0\u0189\u0001\u0000\u0000\u0000"+ + "\u05d1\u05d2\u0003F\u001b\u0000\u05d2\u05d3\u0001\u0000\u0000\u0000\u05d3"+ + "\u05d4\u0006\u00bd\u000b\u0000\u05d4\u018b\u0001\u0000\u0000\u0000\u05d5"+ + "\u05d6\u0003H\u001c\u0000\u05d6\u05d7\u0001\u0000\u0000\u0000\u05d7\u05d8"+ + "\u0006\u00be\u0010\u0000\u05d8\u05d9\u0006\u00be\f\u0000\u05d9\u018d\u0001"+ + "\u0000\u0000\u0000\u05da\u05db\u00036\u0013\u0000\u05db\u05dc\u0001\u0000"+ + "\u0000\u0000\u05dc\u05dd\u0006\u00bf#\u0000\u05dd\u018f\u0001\u0000\u0000"+ + "\u0000\u05de\u05df\u0003\u010c~\u0000\u05df\u05e0\u0001\u0000\u0000\u0000"+ + "\u05e0\u05e1\u0006\u00c0$\u0000\u05e1\u0191\u0001\u0000\u0000\u0000\u05e2"+ + "\u05e3\u0003\u011a\u0085\u0000\u05e3\u05e4\u0001\u0000\u0000\u0000\u05e4"+ + "\u05e5\u0006\u00c1!\u0000\u05e5\u05e6\u0006\u00c1\f\u0000\u05e6\u05e7"+ + "\u0006\u00c1\u0000\u0000\u05e7\u0193\u0001\u0000\u0000\u0000\u05e8\u05e9"+ + "\u0007\u0014\u0000\u0000\u05e9\u05ea\u0007\u0002\u0000\u0000\u05ea\u05eb"+ + "\u0007\u0001\u0000\u0000\u05eb\u05ec\u0007\t\u0000\u0000\u05ec\u05ed\u0007"+ + "\u0011\u0000\u0000\u05ed\u05ee\u0001\u0000\u0000\u0000\u05ee\u05ef\u0006"+ + "\u00c2\f\u0000\u05ef\u05f0\u0006\u00c2\u0000\u0000\u05f0\u0195\u0001\u0000"+ + "\u0000\u0000\u05f1\u05f2\u0003\u00deg\u0000\u05f2\u05f3\u0001\u0000\u0000"+ + "\u0000\u05f3\u05f4\u0006\u00c3\u0015\u0000\u05f4\u0197\u0001\u0000\u0000"+ + "\u0000\u05f5\u05f6\u0003^\'\u0000\u05f6\u05f7\u0001\u0000\u0000\u0000"+ + "\u05f7\u05f8\u0006\u00c4\u0016\u0000\u05f8\u0199\u0001\u0000\u0000\u0000"+ + "\u05f9\u05fa\u0003n/\u0000\u05fa\u05fb\u0001\u0000\u0000\u0000\u05fb\u05fc"+ + "\u0006\u00c5\u0012\u0000\u05fc\u019b\u0001\u0000\u0000\u0000\u05fd\u05fe"+ + "\u0003\u00b8T\u0000\u05fe\u05ff\u0001\u0000\u0000\u0000\u05ff\u0600\u0006"+ + "\u00c6 \u0000\u0600\u019d\u0001\u0000\u0000\u0000\u0601\u0602\u0003\u00bc"+ + "V\u0000\u0602\u0603\u0001\u0000\u0000\u0000\u0603\u0604\u0006\u00c7\u001f"+ + "\u0000\u0604\u019f\u0001\u0000\u0000\u0000\u0605\u0606\u0003B\u0019\u0000"+ + "\u0606\u0607\u0001\u0000\u0000\u0000\u0607\u0608\u0006\u00c8\u000b\u0000"+ + "\u0608\u01a1\u0001\u0000\u0000\u0000\u0609\u060a\u0003D\u001a\u0000\u060a"+ + "\u060b\u0001\u0000\u0000\u0000\u060b\u060c\u0006\u00c9\u000b\u0000\u060c"+ + "\u01a3\u0001\u0000\u0000\u0000\u060d\u060e\u0003F\u001b\u0000\u060e\u060f"+ + "\u0001\u0000\u0000\u0000\u060f\u0610\u0006\u00ca\u000b\u0000\u0610\u01a5"+ + "\u0001\u0000\u0000\u0000\u0611\u0612\u0003H\u001c\u0000\u0612\u0613\u0001"+ + "\u0000\u0000\u0000\u0613\u0614\u0006\u00cb\u0010\u0000\u0614\u0615\u0006"+ + "\u00cb\f\u0000\u0615\u01a7\u0001\u0000\u0000\u0000\u0616\u0617\u0003\u00de"+ + "g\u0000\u0617\u0618\u0001\u0000\u0000\u0000\u0618\u0619\u0006\u00cc\u0015"+ + "\u0000\u0619\u061a\u0006\u00cc\f\u0000\u061a\u061b\u0006\u00cc%\u0000"+ + "\u061b\u01a9\u0001\u0000\u0000\u0000\u061c\u061d\u0003^\'\u0000\u061d"+ + "\u061e\u0001\u0000\u0000\u0000\u061e\u061f\u0006\u00cd\u0016\u0000\u061f"+ + "\u0620\u0006\u00cd\f\u0000\u0620\u0621\u0006\u00cd%\u0000\u0621\u01ab"+ + "\u0001\u0000\u0000\u0000\u0622\u0623\u0003B\u0019\u0000\u0623\u0624\u0001"+ + "\u0000\u0000\u0000\u0624\u0625\u0006\u00ce\u000b\u0000\u0625\u01ad\u0001"+ + "\u0000\u0000\u0000\u0626\u0627\u0003D\u001a\u0000\u0627\u0628\u0001\u0000"+ + "\u0000\u0000\u0628\u0629\u0006\u00cf\u000b\u0000\u0629\u01af\u0001\u0000"+ + "\u0000\u0000\u062a\u062b\u0003F\u001b\u0000\u062b\u062c\u0001\u0000\u0000"+ + "\u0000\u062c\u062d\u0006\u00d0\u000b\u0000\u062d\u01b1\u0001\u0000\u0000"+ + "\u0000\u062e\u062f\u0003n/\u0000\u062f\u0630\u0001\u0000\u0000\u0000\u0630"+ + "\u0631\u0006\u00d1\u0012\u0000\u0631\u0632\u0006\u00d1\f\u0000\u0632\u0633"+ + "\u0006\u00d1\t\u0000\u0633\u01b3\u0001\u0000\u0000\u0000\u0634\u0635\u0003"+ + "p0\u0000\u0635\u0636\u0001\u0000\u0000\u0000\u0636\u0637\u0006\u00d2\u0013"+ + "\u0000\u0637\u0638\u0006\u00d2\f\u0000\u0638\u0639\u0006\u00d2\t\u0000"+ + "\u0639\u01b5\u0001\u0000\u0000\u0000\u063a\u063b\u0003B\u0019\u0000\u063b"+ + "\u063c\u0001\u0000\u0000\u0000\u063c\u063d\u0006\u00d3\u000b\u0000\u063d"+ + "\u01b7\u0001\u0000\u0000\u0000\u063e\u063f\u0003D\u001a\u0000\u063f\u0640"+ + "\u0001\u0000\u0000\u0000\u0640\u0641\u0006\u00d4\u000b\u0000\u0641\u01b9"+ + "\u0001\u0000\u0000\u0000\u0642\u0643\u0003F\u001b\u0000\u0643\u0644\u0001"+ + "\u0000\u0000\u0000\u0644\u0645\u0006\u00d5\u000b\u0000\u0645\u01bb\u0001"+ + "\u0000\u0000\u0000\u0646\u0647\u0003\u00bcV\u0000\u0647\u0648\u0001\u0000"+ + "\u0000\u0000\u0648\u0649\u0006\u00d6\f\u0000\u0649\u064a\u0006\u00d6\u0000"+ + "\u0000\u064a\u064b\u0006\u00d6\u001f\u0000\u064b\u01bd\u0001\u0000\u0000"+ + "\u0000\u064c\u064d\u0003\u00b8T\u0000\u064d\u064e\u0001\u0000\u0000\u0000"+ + "\u064e\u064f\u0006\u00d7\f\u0000\u064f\u0650\u0006\u00d7\u0000\u0000\u0650"+ + "\u0651\u0006\u00d7 \u0000\u0651\u01bf\u0001\u0000\u0000\u0000\u0652\u0653"+ + "\u0003d*\u0000\u0653\u0654\u0001\u0000\u0000\u0000\u0654\u0655\u0006\u00d8"+ + "\f\u0000\u0655\u0656\u0006\u00d8\u0000\u0000\u0656\u0657\u0006\u00d8&"+ + "\u0000\u0657\u01c1\u0001\u0000\u0000\u0000\u0658\u0659\u0003H\u001c\u0000"+ + "\u0659\u065a\u0001\u0000\u0000\u0000\u065a\u065b\u0006\u00d9\u0010\u0000"+ + "\u065b\u065c\u0006\u00d9\f\u0000\u065c\u01c3\u0001\u0000\u0000\u0000B"+ + "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e"+ + "\u000f\u0299\u02a3\u02a7\u02aa\u02b3\u02b5\u02c0\u02d3\u02d8\u02e1\u02e8"+ + "\u02ed\u02ef\u02fa\u0302\u0305\u0307\u030c\u0311\u0317\u031e\u0323\u0329"+ + "\u032c\u0334\u0338\u03be\u03c3\u03ca\u03cc\u03dc\u03e1\u03e6\u03e8\u03ee"+ + "\u043b\u0440\u0471\u0475\u047a\u047f\u0484\u0486\u048a\u048c\u04e3\u04e7"+ + "\u04ec\u057d\u057f\'\u0005\u0001\u0000\u0005\u0004\u0000\u0005\u0006\u0000"+ + "\u0005\u0002\u0000\u0005\u0003\u0000\u0005\b\u0000\u0005\u0005\u0000\u0005"+ + "\t\u0000\u0005\u000b\u0000\u0005\u000e\u0000\u0005\r\u0000\u0000\u0001"+ + "\u0000\u0004\u0000\u0000\u0007\u0010\u0000\u0007H\u0000\u0005\u0000\u0000"+ + "\u0007\u001d\u0000\u0007I\u0000\u0007&\u0000\u0007\'\u0000\u0007$\u0000"+ + "\u0007S\u0000\u0007\u001e\u0000\u0007)\u0000\u00075\u0000\u0007G\u0000"+ + "\u0007W\u0000\u0005\n\u0000\u0005\u0007\u0000\u0007a\u0000\u0007`\u0000"+ + "\u0007K\u0000\u0007J\u0000\u0007_\u0000\u0005\f\u0000\u0007\u0014\u0000"+ + "\u0007[\u0000\u0005\u000f\u0000\u0007!\u0000"; public static final ATN _ATN = new ATNDeserializer().deserialize(_serializedATN.toCharArray()); static { diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParser.interp b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParser.interp index c9db129e08ba2..9bed77ff31168 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParser.interp +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParser.interp @@ -334,4 +334,4 @@ joinPredicate atn: -[4, 1, 130, 651, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 142, 8, 1, 10, 1, 12, 1, 145, 9, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 3, 2, 153, 8, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 3, 3, 173, 8, 3, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 185, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 5, 5, 192, 8, 5, 10, 5, 12, 5, 195, 9, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 202, 8, 5, 1, 5, 1, 5, 1, 5, 3, 5, 207, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 5, 5, 215, 8, 5, 10, 5, 12, 5, 218, 9, 5, 1, 6, 1, 6, 3, 6, 222, 8, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 3, 6, 229, 8, 6, 1, 6, 1, 6, 1, 6, 3, 6, 234, 8, 6, 1, 7, 1, 7, 1, 7, 3, 7, 239, 8, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 3, 8, 249, 8, 8, 1, 9, 1, 9, 1, 9, 1, 9, 3, 9, 255, 8, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 5, 9, 263, 8, 9, 10, 9, 12, 9, 266, 9, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 3, 10, 276, 8, 10, 1, 10, 1, 10, 1, 10, 5, 10, 281, 8, 10, 10, 10, 12, 10, 284, 9, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 5, 11, 292, 8, 11, 10, 11, 12, 11, 295, 9, 11, 1, 11, 1, 11, 3, 11, 299, 8, 11, 3, 11, 301, 8, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 5, 13, 312, 8, 13, 10, 13, 12, 13, 315, 9, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 5, 17, 331, 8, 17, 10, 17, 12, 17, 334, 9, 17, 1, 18, 1, 18, 1, 18, 3, 18, 339, 8, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 5, 19, 347, 8, 19, 10, 19, 12, 19, 350, 9, 19, 1, 19, 3, 19, 353, 8, 19, 1, 20, 1, 20, 1, 20, 3, 20, 358, 8, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 23, 5, 23, 370, 8, 23, 10, 23, 12, 23, 373, 9, 23, 1, 24, 1, 24, 1, 24, 1, 24, 5, 24, 379, 8, 24, 10, 24, 12, 24, 382, 9, 24, 1, 24, 3, 24, 385, 8, 24, 1, 24, 1, 24, 3, 24, 389, 8, 24, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 3, 26, 396, 8, 26, 1, 26, 1, 26, 3, 26, 400, 8, 26, 1, 27, 1, 27, 1, 27, 5, 27, 405, 8, 27, 10, 27, 12, 27, 408, 9, 27, 1, 28, 1, 28, 1, 28, 3, 28, 413, 8, 28, 1, 29, 1, 29, 1, 29, 5, 29, 418, 8, 29, 10, 29, 12, 29, 421, 9, 29, 1, 30, 1, 30, 1, 30, 5, 30, 426, 8, 30, 10, 30, 12, 30, 429, 9, 30, 1, 31, 1, 31, 1, 31, 5, 31, 434, 8, 31, 10, 31, 12, 31, 437, 9, 31, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 3, 33, 444, 8, 33, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 5, 34, 459, 8, 34, 10, 34, 12, 34, 462, 9, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 5, 34, 470, 8, 34, 10, 34, 12, 34, 473, 9, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 5, 34, 481, 8, 34, 10, 34, 12, 34, 484, 9, 34, 1, 34, 1, 34, 3, 34, 488, 8, 34, 1, 35, 1, 35, 3, 35, 492, 8, 35, 1, 36, 1, 36, 1, 36, 3, 36, 497, 8, 36, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 38, 5, 38, 506, 8, 38, 10, 38, 12, 38, 509, 9, 38, 1, 39, 1, 39, 3, 39, 513, 8, 39, 1, 39, 1, 39, 3, 39, 517, 8, 39, 1, 40, 1, 40, 1, 40, 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, 42, 1, 42, 5, 42, 529, 8, 42, 10, 42, 12, 42, 532, 9, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 44, 1, 44, 1, 44, 1, 44, 3, 44, 542, 8, 44, 1, 45, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 5, 47, 554, 8, 47, 10, 47, 12, 47, 557, 9, 47, 1, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1, 50, 1, 50, 3, 50, 567, 8, 50, 1, 51, 3, 51, 570, 8, 51, 1, 51, 1, 51, 1, 52, 3, 52, 575, 8, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 56, 1, 56, 1, 56, 1, 56, 1, 57, 1, 57, 1, 57, 1, 58, 1, 58, 1, 58, 1, 58, 3, 58, 597, 8, 58, 1, 58, 1, 58, 1, 58, 1, 58, 5, 58, 603, 8, 58, 10, 58, 12, 58, 606, 9, 58, 3, 58, 608, 8, 58, 1, 59, 1, 59, 1, 59, 3, 59, 613, 8, 59, 1, 59, 1, 59, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 61, 1, 61, 1, 61, 1, 61, 3, 61, 626, 8, 61, 1, 62, 3, 62, 629, 8, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 63, 1, 63, 1, 63, 3, 63, 638, 8, 63, 1, 64, 1, 64, 1, 64, 1, 64, 5, 64, 644, 8, 64, 10, 64, 12, 64, 647, 9, 64, 1, 65, 1, 65, 1, 65, 0, 4, 2, 10, 18, 20, 66, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 0, 9, 1, 0, 64, 65, 1, 0, 66, 68, 2, 0, 30, 30, 83, 83, 1, 0, 74, 75, 2, 0, 35, 35, 40, 40, 2, 0, 43, 43, 46, 46, 2, 0, 42, 42, 56, 56, 2, 0, 57, 57, 59, 63, 1, 0, 22, 24, 678, 0, 132, 1, 0, 0, 0, 2, 135, 1, 0, 0, 0, 4, 152, 1, 0, 0, 0, 6, 172, 1, 0, 0, 0, 8, 174, 1, 0, 0, 0, 10, 206, 1, 0, 0, 0, 12, 233, 1, 0, 0, 0, 14, 235, 1, 0, 0, 0, 16, 248, 1, 0, 0, 0, 18, 254, 1, 0, 0, 0, 20, 275, 1, 0, 0, 0, 22, 285, 1, 0, 0, 0, 24, 304, 1, 0, 0, 0, 26, 306, 1, 0, 0, 0, 28, 318, 1, 0, 0, 0, 30, 322, 1, 0, 0, 0, 32, 324, 1, 0, 0, 0, 34, 327, 1, 0, 0, 0, 36, 338, 1, 0, 0, 0, 38, 342, 1, 0, 0, 0, 40, 357, 1, 0, 0, 0, 42, 361, 1, 0, 0, 0, 44, 363, 1, 0, 0, 0, 46, 365, 1, 0, 0, 0, 48, 374, 1, 0, 0, 0, 50, 390, 1, 0, 0, 0, 52, 393, 1, 0, 0, 0, 54, 401, 1, 0, 0, 0, 56, 409, 1, 0, 0, 0, 58, 414, 1, 0, 0, 0, 60, 422, 1, 0, 0, 0, 62, 430, 1, 0, 0, 0, 64, 438, 1, 0, 0, 0, 66, 443, 1, 0, 0, 0, 68, 487, 1, 0, 0, 0, 70, 491, 1, 0, 0, 0, 72, 496, 1, 0, 0, 0, 74, 498, 1, 0, 0, 0, 76, 501, 1, 0, 0, 0, 78, 510, 1, 0, 0, 0, 80, 518, 1, 0, 0, 0, 82, 521, 1, 0, 0, 0, 84, 524, 1, 0, 0, 0, 86, 533, 1, 0, 0, 0, 88, 537, 1, 0, 0, 0, 90, 543, 1, 0, 0, 0, 92, 547, 1, 0, 0, 0, 94, 550, 1, 0, 0, 0, 96, 558, 1, 0, 0, 0, 98, 562, 1, 0, 0, 0, 100, 566, 1, 0, 0, 0, 102, 569, 1, 0, 0, 0, 104, 574, 1, 0, 0, 0, 106, 578, 1, 0, 0, 0, 108, 580, 1, 0, 0, 0, 110, 582, 1, 0, 0, 0, 112, 585, 1, 0, 0, 0, 114, 589, 1, 0, 0, 0, 116, 592, 1, 0, 0, 0, 118, 612, 1, 0, 0, 0, 120, 616, 1, 0, 0, 0, 122, 621, 1, 0, 0, 0, 124, 628, 1, 0, 0, 0, 126, 634, 1, 0, 0, 0, 128, 639, 1, 0, 0, 0, 130, 648, 1, 0, 0, 0, 132, 133, 3, 2, 1, 0, 133, 134, 5, 0, 0, 1, 134, 1, 1, 0, 0, 0, 135, 136, 6, 1, -1, 0, 136, 137, 3, 4, 2, 0, 137, 143, 1, 0, 0, 0, 138, 139, 10, 1, 0, 0, 139, 140, 5, 29, 0, 0, 140, 142, 3, 6, 3, 0, 141, 138, 1, 0, 0, 0, 142, 145, 1, 0, 0, 0, 143, 141, 1, 0, 0, 0, 143, 144, 1, 0, 0, 0, 144, 3, 1, 0, 0, 0, 145, 143, 1, 0, 0, 0, 146, 153, 3, 110, 55, 0, 147, 153, 3, 38, 19, 0, 148, 153, 3, 32, 16, 0, 149, 153, 3, 114, 57, 0, 150, 151, 4, 2, 1, 0, 151, 153, 3, 48, 24, 0, 152, 146, 1, 0, 0, 0, 152, 147, 1, 0, 0, 0, 152, 148, 1, 0, 0, 0, 152, 149, 1, 0, 0, 0, 152, 150, 1, 0, 0, 0, 153, 5, 1, 0, 0, 0, 154, 173, 3, 50, 25, 0, 155, 173, 3, 8, 4, 0, 156, 173, 3, 80, 40, 0, 157, 173, 3, 74, 37, 0, 158, 173, 3, 52, 26, 0, 159, 173, 3, 76, 38, 0, 160, 173, 3, 82, 41, 0, 161, 173, 3, 84, 42, 0, 162, 173, 3, 88, 44, 0, 163, 173, 3, 90, 45, 0, 164, 173, 3, 116, 58, 0, 165, 173, 3, 92, 46, 0, 166, 167, 4, 3, 2, 0, 167, 173, 3, 122, 61, 0, 168, 169, 4, 3, 3, 0, 169, 173, 3, 120, 60, 0, 170, 171, 4, 3, 4, 0, 171, 173, 3, 124, 62, 0, 172, 154, 1, 0, 0, 0, 172, 155, 1, 0, 0, 0, 172, 156, 1, 0, 0, 0, 172, 157, 1, 0, 0, 0, 172, 158, 1, 0, 0, 0, 172, 159, 1, 0, 0, 0, 172, 160, 1, 0, 0, 0, 172, 161, 1, 0, 0, 0, 172, 162, 1, 0, 0, 0, 172, 163, 1, 0, 0, 0, 172, 164, 1, 0, 0, 0, 172, 165, 1, 0, 0, 0, 172, 166, 1, 0, 0, 0, 172, 168, 1, 0, 0, 0, 172, 170, 1, 0, 0, 0, 173, 7, 1, 0, 0, 0, 174, 175, 5, 16, 0, 0, 175, 176, 3, 10, 5, 0, 176, 9, 1, 0, 0, 0, 177, 178, 6, 5, -1, 0, 178, 179, 5, 49, 0, 0, 179, 207, 3, 10, 5, 8, 180, 207, 3, 16, 8, 0, 181, 207, 3, 12, 6, 0, 182, 184, 3, 16, 8, 0, 183, 185, 5, 49, 0, 0, 184, 183, 1, 0, 0, 0, 184, 185, 1, 0, 0, 0, 185, 186, 1, 0, 0, 0, 186, 187, 5, 44, 0, 0, 187, 188, 5, 48, 0, 0, 188, 193, 3, 16, 8, 0, 189, 190, 5, 39, 0, 0, 190, 192, 3, 16, 8, 0, 191, 189, 1, 0, 0, 0, 192, 195, 1, 0, 0, 0, 193, 191, 1, 0, 0, 0, 193, 194, 1, 0, 0, 0, 194, 196, 1, 0, 0, 0, 195, 193, 1, 0, 0, 0, 196, 197, 5, 55, 0, 0, 197, 207, 1, 0, 0, 0, 198, 199, 3, 16, 8, 0, 199, 201, 5, 45, 0, 0, 200, 202, 5, 49, 0, 0, 201, 200, 1, 0, 0, 0, 201, 202, 1, 0, 0, 0, 202, 203, 1, 0, 0, 0, 203, 204, 5, 50, 0, 0, 204, 207, 1, 0, 0, 0, 205, 207, 3, 14, 7, 0, 206, 177, 1, 0, 0, 0, 206, 180, 1, 0, 0, 0, 206, 181, 1, 0, 0, 0, 206, 182, 1, 0, 0, 0, 206, 198, 1, 0, 0, 0, 206, 205, 1, 0, 0, 0, 207, 216, 1, 0, 0, 0, 208, 209, 10, 5, 0, 0, 209, 210, 5, 34, 0, 0, 210, 215, 3, 10, 5, 6, 211, 212, 10, 4, 0, 0, 212, 213, 5, 52, 0, 0, 213, 215, 3, 10, 5, 5, 214, 208, 1, 0, 0, 0, 214, 211, 1, 0, 0, 0, 215, 218, 1, 0, 0, 0, 216, 214, 1, 0, 0, 0, 216, 217, 1, 0, 0, 0, 217, 11, 1, 0, 0, 0, 218, 216, 1, 0, 0, 0, 219, 221, 3, 16, 8, 0, 220, 222, 5, 49, 0, 0, 221, 220, 1, 0, 0, 0, 221, 222, 1, 0, 0, 0, 222, 223, 1, 0, 0, 0, 223, 224, 5, 47, 0, 0, 224, 225, 3, 106, 53, 0, 225, 234, 1, 0, 0, 0, 226, 228, 3, 16, 8, 0, 227, 229, 5, 49, 0, 0, 228, 227, 1, 0, 0, 0, 228, 229, 1, 0, 0, 0, 229, 230, 1, 0, 0, 0, 230, 231, 5, 54, 0, 0, 231, 232, 3, 106, 53, 0, 232, 234, 1, 0, 0, 0, 233, 219, 1, 0, 0, 0, 233, 226, 1, 0, 0, 0, 234, 13, 1, 0, 0, 0, 235, 238, 3, 58, 29, 0, 236, 237, 5, 37, 0, 0, 237, 239, 3, 30, 15, 0, 238, 236, 1, 0, 0, 0, 238, 239, 1, 0, 0, 0, 239, 240, 1, 0, 0, 0, 240, 241, 5, 38, 0, 0, 241, 242, 3, 68, 34, 0, 242, 15, 1, 0, 0, 0, 243, 249, 3, 18, 9, 0, 244, 245, 3, 18, 9, 0, 245, 246, 3, 108, 54, 0, 246, 247, 3, 18, 9, 0, 247, 249, 1, 0, 0, 0, 248, 243, 1, 0, 0, 0, 248, 244, 1, 0, 0, 0, 249, 17, 1, 0, 0, 0, 250, 251, 6, 9, -1, 0, 251, 255, 3, 20, 10, 0, 252, 253, 7, 0, 0, 0, 253, 255, 3, 18, 9, 3, 254, 250, 1, 0, 0, 0, 254, 252, 1, 0, 0, 0, 255, 264, 1, 0, 0, 0, 256, 257, 10, 2, 0, 0, 257, 258, 7, 1, 0, 0, 258, 263, 3, 18, 9, 3, 259, 260, 10, 1, 0, 0, 260, 261, 7, 0, 0, 0, 261, 263, 3, 18, 9, 2, 262, 256, 1, 0, 0, 0, 262, 259, 1, 0, 0, 0, 263, 266, 1, 0, 0, 0, 264, 262, 1, 0, 0, 0, 264, 265, 1, 0, 0, 0, 265, 19, 1, 0, 0, 0, 266, 264, 1, 0, 0, 0, 267, 268, 6, 10, -1, 0, 268, 276, 3, 68, 34, 0, 269, 276, 3, 58, 29, 0, 270, 276, 3, 22, 11, 0, 271, 272, 5, 48, 0, 0, 272, 273, 3, 10, 5, 0, 273, 274, 5, 55, 0, 0, 274, 276, 1, 0, 0, 0, 275, 267, 1, 0, 0, 0, 275, 269, 1, 0, 0, 0, 275, 270, 1, 0, 0, 0, 275, 271, 1, 0, 0, 0, 276, 282, 1, 0, 0, 0, 277, 278, 10, 1, 0, 0, 278, 279, 5, 37, 0, 0, 279, 281, 3, 30, 15, 0, 280, 277, 1, 0, 0, 0, 281, 284, 1, 0, 0, 0, 282, 280, 1, 0, 0, 0, 282, 283, 1, 0, 0, 0, 283, 21, 1, 0, 0, 0, 284, 282, 1, 0, 0, 0, 285, 286, 3, 24, 12, 0, 286, 300, 5, 48, 0, 0, 287, 301, 5, 66, 0, 0, 288, 293, 3, 10, 5, 0, 289, 290, 5, 39, 0, 0, 290, 292, 3, 10, 5, 0, 291, 289, 1, 0, 0, 0, 292, 295, 1, 0, 0, 0, 293, 291, 1, 0, 0, 0, 293, 294, 1, 0, 0, 0, 294, 298, 1, 0, 0, 0, 295, 293, 1, 0, 0, 0, 296, 297, 5, 39, 0, 0, 297, 299, 3, 26, 13, 0, 298, 296, 1, 0, 0, 0, 298, 299, 1, 0, 0, 0, 299, 301, 1, 0, 0, 0, 300, 287, 1, 0, 0, 0, 300, 288, 1, 0, 0, 0, 300, 301, 1, 0, 0, 0, 301, 302, 1, 0, 0, 0, 302, 303, 5, 55, 0, 0, 303, 23, 1, 0, 0, 0, 304, 305, 3, 72, 36, 0, 305, 25, 1, 0, 0, 0, 306, 307, 4, 13, 10, 0, 307, 308, 5, 69, 0, 0, 308, 313, 3, 28, 14, 0, 309, 310, 5, 39, 0, 0, 310, 312, 3, 28, 14, 0, 311, 309, 1, 0, 0, 0, 312, 315, 1, 0, 0, 0, 313, 311, 1, 0, 0, 0, 313, 314, 1, 0, 0, 0, 314, 316, 1, 0, 0, 0, 315, 313, 1, 0, 0, 0, 316, 317, 5, 70, 0, 0, 317, 27, 1, 0, 0, 0, 318, 319, 3, 106, 53, 0, 319, 320, 5, 38, 0, 0, 320, 321, 3, 68, 34, 0, 321, 29, 1, 0, 0, 0, 322, 323, 3, 64, 32, 0, 323, 31, 1, 0, 0, 0, 324, 325, 5, 12, 0, 0, 325, 326, 3, 34, 17, 0, 326, 33, 1, 0, 0, 0, 327, 332, 3, 36, 18, 0, 328, 329, 5, 39, 0, 0, 329, 331, 3, 36, 18, 0, 330, 328, 1, 0, 0, 0, 331, 334, 1, 0, 0, 0, 332, 330, 1, 0, 0, 0, 332, 333, 1, 0, 0, 0, 333, 35, 1, 0, 0, 0, 334, 332, 1, 0, 0, 0, 335, 336, 3, 58, 29, 0, 336, 337, 5, 36, 0, 0, 337, 339, 1, 0, 0, 0, 338, 335, 1, 0, 0, 0, 338, 339, 1, 0, 0, 0, 339, 340, 1, 0, 0, 0, 340, 341, 3, 10, 5, 0, 341, 37, 1, 0, 0, 0, 342, 343, 5, 6, 0, 0, 343, 348, 3, 40, 20, 0, 344, 345, 5, 39, 0, 0, 345, 347, 3, 40, 20, 0, 346, 344, 1, 0, 0, 0, 347, 350, 1, 0, 0, 0, 348, 346, 1, 0, 0, 0, 348, 349, 1, 0, 0, 0, 349, 352, 1, 0, 0, 0, 350, 348, 1, 0, 0, 0, 351, 353, 3, 46, 23, 0, 352, 351, 1, 0, 0, 0, 352, 353, 1, 0, 0, 0, 353, 39, 1, 0, 0, 0, 354, 355, 3, 42, 21, 0, 355, 356, 5, 38, 0, 0, 356, 358, 1, 0, 0, 0, 357, 354, 1, 0, 0, 0, 357, 358, 1, 0, 0, 0, 358, 359, 1, 0, 0, 0, 359, 360, 3, 44, 22, 0, 360, 41, 1, 0, 0, 0, 361, 362, 5, 83, 0, 0, 362, 43, 1, 0, 0, 0, 363, 364, 7, 2, 0, 0, 364, 45, 1, 0, 0, 0, 365, 366, 5, 82, 0, 0, 366, 371, 5, 83, 0, 0, 367, 368, 5, 39, 0, 0, 368, 370, 5, 83, 0, 0, 369, 367, 1, 0, 0, 0, 370, 373, 1, 0, 0, 0, 371, 369, 1, 0, 0, 0, 371, 372, 1, 0, 0, 0, 372, 47, 1, 0, 0, 0, 373, 371, 1, 0, 0, 0, 374, 375, 5, 19, 0, 0, 375, 380, 3, 40, 20, 0, 376, 377, 5, 39, 0, 0, 377, 379, 3, 40, 20, 0, 378, 376, 1, 0, 0, 0, 379, 382, 1, 0, 0, 0, 380, 378, 1, 0, 0, 0, 380, 381, 1, 0, 0, 0, 381, 384, 1, 0, 0, 0, 382, 380, 1, 0, 0, 0, 383, 385, 3, 54, 27, 0, 384, 383, 1, 0, 0, 0, 384, 385, 1, 0, 0, 0, 385, 388, 1, 0, 0, 0, 386, 387, 5, 33, 0, 0, 387, 389, 3, 34, 17, 0, 388, 386, 1, 0, 0, 0, 388, 389, 1, 0, 0, 0, 389, 49, 1, 0, 0, 0, 390, 391, 5, 4, 0, 0, 391, 392, 3, 34, 17, 0, 392, 51, 1, 0, 0, 0, 393, 395, 5, 15, 0, 0, 394, 396, 3, 54, 27, 0, 395, 394, 1, 0, 0, 0, 395, 396, 1, 0, 0, 0, 396, 399, 1, 0, 0, 0, 397, 398, 5, 33, 0, 0, 398, 400, 3, 34, 17, 0, 399, 397, 1, 0, 0, 0, 399, 400, 1, 0, 0, 0, 400, 53, 1, 0, 0, 0, 401, 406, 3, 56, 28, 0, 402, 403, 5, 39, 0, 0, 403, 405, 3, 56, 28, 0, 404, 402, 1, 0, 0, 0, 405, 408, 1, 0, 0, 0, 406, 404, 1, 0, 0, 0, 406, 407, 1, 0, 0, 0, 407, 55, 1, 0, 0, 0, 408, 406, 1, 0, 0, 0, 409, 412, 3, 36, 18, 0, 410, 411, 5, 16, 0, 0, 411, 413, 3, 10, 5, 0, 412, 410, 1, 0, 0, 0, 412, 413, 1, 0, 0, 0, 413, 57, 1, 0, 0, 0, 414, 419, 3, 72, 36, 0, 415, 416, 5, 41, 0, 0, 416, 418, 3, 72, 36, 0, 417, 415, 1, 0, 0, 0, 418, 421, 1, 0, 0, 0, 419, 417, 1, 0, 0, 0, 419, 420, 1, 0, 0, 0, 420, 59, 1, 0, 0, 0, 421, 419, 1, 0, 0, 0, 422, 427, 3, 66, 33, 0, 423, 424, 5, 41, 0, 0, 424, 426, 3, 66, 33, 0, 425, 423, 1, 0, 0, 0, 426, 429, 1, 0, 0, 0, 427, 425, 1, 0, 0, 0, 427, 428, 1, 0, 0, 0, 428, 61, 1, 0, 0, 0, 429, 427, 1, 0, 0, 0, 430, 435, 3, 60, 30, 0, 431, 432, 5, 39, 0, 0, 432, 434, 3, 60, 30, 0, 433, 431, 1, 0, 0, 0, 434, 437, 1, 0, 0, 0, 435, 433, 1, 0, 0, 0, 435, 436, 1, 0, 0, 0, 436, 63, 1, 0, 0, 0, 437, 435, 1, 0, 0, 0, 438, 439, 7, 3, 0, 0, 439, 65, 1, 0, 0, 0, 440, 444, 5, 87, 0, 0, 441, 442, 4, 33, 11, 0, 442, 444, 3, 70, 35, 0, 443, 440, 1, 0, 0, 0, 443, 441, 1, 0, 0, 0, 444, 67, 1, 0, 0, 0, 445, 488, 5, 50, 0, 0, 446, 447, 3, 104, 52, 0, 447, 448, 5, 74, 0, 0, 448, 488, 1, 0, 0, 0, 449, 488, 3, 102, 51, 0, 450, 488, 3, 104, 52, 0, 451, 488, 3, 98, 49, 0, 452, 488, 3, 70, 35, 0, 453, 488, 3, 106, 53, 0, 454, 455, 5, 72, 0, 0, 455, 460, 3, 100, 50, 0, 456, 457, 5, 39, 0, 0, 457, 459, 3, 100, 50, 0, 458, 456, 1, 0, 0, 0, 459, 462, 1, 0, 0, 0, 460, 458, 1, 0, 0, 0, 460, 461, 1, 0, 0, 0, 461, 463, 1, 0, 0, 0, 462, 460, 1, 0, 0, 0, 463, 464, 5, 73, 0, 0, 464, 488, 1, 0, 0, 0, 465, 466, 5, 72, 0, 0, 466, 471, 3, 98, 49, 0, 467, 468, 5, 39, 0, 0, 468, 470, 3, 98, 49, 0, 469, 467, 1, 0, 0, 0, 470, 473, 1, 0, 0, 0, 471, 469, 1, 0, 0, 0, 471, 472, 1, 0, 0, 0, 472, 474, 1, 0, 0, 0, 473, 471, 1, 0, 0, 0, 474, 475, 5, 73, 0, 0, 475, 488, 1, 0, 0, 0, 476, 477, 5, 72, 0, 0, 477, 482, 3, 106, 53, 0, 478, 479, 5, 39, 0, 0, 479, 481, 3, 106, 53, 0, 480, 478, 1, 0, 0, 0, 481, 484, 1, 0, 0, 0, 482, 480, 1, 0, 0, 0, 482, 483, 1, 0, 0, 0, 483, 485, 1, 0, 0, 0, 484, 482, 1, 0, 0, 0, 485, 486, 5, 73, 0, 0, 486, 488, 1, 0, 0, 0, 487, 445, 1, 0, 0, 0, 487, 446, 1, 0, 0, 0, 487, 449, 1, 0, 0, 0, 487, 450, 1, 0, 0, 0, 487, 451, 1, 0, 0, 0, 487, 452, 1, 0, 0, 0, 487, 453, 1, 0, 0, 0, 487, 454, 1, 0, 0, 0, 487, 465, 1, 0, 0, 0, 487, 476, 1, 0, 0, 0, 488, 69, 1, 0, 0, 0, 489, 492, 5, 53, 0, 0, 490, 492, 5, 71, 0, 0, 491, 489, 1, 0, 0, 0, 491, 490, 1, 0, 0, 0, 492, 71, 1, 0, 0, 0, 493, 497, 3, 64, 32, 0, 494, 495, 4, 36, 12, 0, 495, 497, 3, 70, 35, 0, 496, 493, 1, 0, 0, 0, 496, 494, 1, 0, 0, 0, 497, 73, 1, 0, 0, 0, 498, 499, 5, 9, 0, 0, 499, 500, 5, 31, 0, 0, 500, 75, 1, 0, 0, 0, 501, 502, 5, 14, 0, 0, 502, 507, 3, 78, 39, 0, 503, 504, 5, 39, 0, 0, 504, 506, 3, 78, 39, 0, 505, 503, 1, 0, 0, 0, 506, 509, 1, 0, 0, 0, 507, 505, 1, 0, 0, 0, 507, 508, 1, 0, 0, 0, 508, 77, 1, 0, 0, 0, 509, 507, 1, 0, 0, 0, 510, 512, 3, 10, 5, 0, 511, 513, 7, 4, 0, 0, 512, 511, 1, 0, 0, 0, 512, 513, 1, 0, 0, 0, 513, 516, 1, 0, 0, 0, 514, 515, 5, 51, 0, 0, 515, 517, 7, 5, 0, 0, 516, 514, 1, 0, 0, 0, 516, 517, 1, 0, 0, 0, 517, 79, 1, 0, 0, 0, 518, 519, 5, 8, 0, 0, 519, 520, 3, 62, 31, 0, 520, 81, 1, 0, 0, 0, 521, 522, 5, 2, 0, 0, 522, 523, 3, 62, 31, 0, 523, 83, 1, 0, 0, 0, 524, 525, 5, 11, 0, 0, 525, 530, 3, 86, 43, 0, 526, 527, 5, 39, 0, 0, 527, 529, 3, 86, 43, 0, 528, 526, 1, 0, 0, 0, 529, 532, 1, 0, 0, 0, 530, 528, 1, 0, 0, 0, 530, 531, 1, 0, 0, 0, 531, 85, 1, 0, 0, 0, 532, 530, 1, 0, 0, 0, 533, 534, 3, 60, 30, 0, 534, 535, 5, 91, 0, 0, 535, 536, 3, 60, 30, 0, 536, 87, 1, 0, 0, 0, 537, 538, 5, 1, 0, 0, 538, 539, 3, 20, 10, 0, 539, 541, 3, 106, 53, 0, 540, 542, 3, 94, 47, 0, 541, 540, 1, 0, 0, 0, 541, 542, 1, 0, 0, 0, 542, 89, 1, 0, 0, 0, 543, 544, 5, 7, 0, 0, 544, 545, 3, 20, 10, 0, 545, 546, 3, 106, 53, 0, 546, 91, 1, 0, 0, 0, 547, 548, 5, 10, 0, 0, 548, 549, 3, 58, 29, 0, 549, 93, 1, 0, 0, 0, 550, 555, 3, 96, 48, 0, 551, 552, 5, 39, 0, 0, 552, 554, 3, 96, 48, 0, 553, 551, 1, 0, 0, 0, 554, 557, 1, 0, 0, 0, 555, 553, 1, 0, 0, 0, 555, 556, 1, 0, 0, 0, 556, 95, 1, 0, 0, 0, 557, 555, 1, 0, 0, 0, 558, 559, 3, 64, 32, 0, 559, 560, 5, 36, 0, 0, 560, 561, 3, 68, 34, 0, 561, 97, 1, 0, 0, 0, 562, 563, 7, 6, 0, 0, 563, 99, 1, 0, 0, 0, 564, 567, 3, 102, 51, 0, 565, 567, 3, 104, 52, 0, 566, 564, 1, 0, 0, 0, 566, 565, 1, 0, 0, 0, 567, 101, 1, 0, 0, 0, 568, 570, 7, 0, 0, 0, 569, 568, 1, 0, 0, 0, 569, 570, 1, 0, 0, 0, 570, 571, 1, 0, 0, 0, 571, 572, 5, 32, 0, 0, 572, 103, 1, 0, 0, 0, 573, 575, 7, 0, 0, 0, 574, 573, 1, 0, 0, 0, 574, 575, 1, 0, 0, 0, 575, 576, 1, 0, 0, 0, 576, 577, 5, 31, 0, 0, 577, 105, 1, 0, 0, 0, 578, 579, 5, 30, 0, 0, 579, 107, 1, 0, 0, 0, 580, 581, 7, 7, 0, 0, 581, 109, 1, 0, 0, 0, 582, 583, 5, 5, 0, 0, 583, 584, 3, 112, 56, 0, 584, 111, 1, 0, 0, 0, 585, 586, 5, 72, 0, 0, 586, 587, 3, 2, 1, 0, 587, 588, 5, 73, 0, 0, 588, 113, 1, 0, 0, 0, 589, 590, 5, 13, 0, 0, 590, 591, 5, 107, 0, 0, 591, 115, 1, 0, 0, 0, 592, 593, 5, 3, 0, 0, 593, 596, 5, 97, 0, 0, 594, 595, 5, 95, 0, 0, 595, 597, 3, 60, 30, 0, 596, 594, 1, 0, 0, 0, 596, 597, 1, 0, 0, 0, 597, 607, 1, 0, 0, 0, 598, 599, 5, 96, 0, 0, 599, 604, 3, 118, 59, 0, 600, 601, 5, 39, 0, 0, 601, 603, 3, 118, 59, 0, 602, 600, 1, 0, 0, 0, 603, 606, 1, 0, 0, 0, 604, 602, 1, 0, 0, 0, 604, 605, 1, 0, 0, 0, 605, 608, 1, 0, 0, 0, 606, 604, 1, 0, 0, 0, 607, 598, 1, 0, 0, 0, 607, 608, 1, 0, 0, 0, 608, 117, 1, 0, 0, 0, 609, 610, 3, 60, 30, 0, 610, 611, 5, 36, 0, 0, 611, 613, 1, 0, 0, 0, 612, 609, 1, 0, 0, 0, 612, 613, 1, 0, 0, 0, 613, 614, 1, 0, 0, 0, 614, 615, 3, 60, 30, 0, 615, 119, 1, 0, 0, 0, 616, 617, 5, 18, 0, 0, 617, 618, 3, 40, 20, 0, 618, 619, 5, 95, 0, 0, 619, 620, 3, 62, 31, 0, 620, 121, 1, 0, 0, 0, 621, 622, 5, 17, 0, 0, 622, 625, 3, 54, 27, 0, 623, 624, 5, 33, 0, 0, 624, 626, 3, 34, 17, 0, 625, 623, 1, 0, 0, 0, 625, 626, 1, 0, 0, 0, 626, 123, 1, 0, 0, 0, 627, 629, 7, 8, 0, 0, 628, 627, 1, 0, 0, 0, 628, 629, 1, 0, 0, 0, 629, 630, 1, 0, 0, 0, 630, 631, 5, 20, 0, 0, 631, 632, 3, 126, 63, 0, 632, 633, 3, 128, 64, 0, 633, 125, 1, 0, 0, 0, 634, 637, 3, 64, 32, 0, 635, 636, 5, 91, 0, 0, 636, 638, 3, 64, 32, 0, 637, 635, 1, 0, 0, 0, 637, 638, 1, 0, 0, 0, 638, 127, 1, 0, 0, 0, 639, 640, 5, 95, 0, 0, 640, 645, 3, 130, 65, 0, 641, 642, 5, 39, 0, 0, 642, 644, 3, 130, 65, 0, 643, 641, 1, 0, 0, 0, 644, 647, 1, 0, 0, 0, 645, 643, 1, 0, 0, 0, 645, 646, 1, 0, 0, 0, 646, 129, 1, 0, 0, 0, 647, 645, 1, 0, 0, 0, 648, 649, 3, 16, 8, 0, 649, 131, 1, 0, 0, 0, 63, 143, 152, 172, 184, 193, 201, 206, 214, 216, 221, 228, 233, 238, 248, 254, 262, 264, 275, 282, 293, 298, 300, 313, 332, 338, 348, 352, 357, 371, 380, 384, 388, 395, 399, 406, 412, 419, 427, 435, 443, 460, 471, 482, 487, 491, 496, 507, 512, 516, 530, 541, 555, 566, 569, 574, 596, 604, 607, 612, 625, 628, 637, 645] \ No newline at end of file +[4, 1, 130, 651, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 142, 8, 1, 10, 1, 12, 1, 145, 9, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 3, 2, 153, 8, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 3, 3, 173, 8, 3, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 185, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 5, 5, 192, 8, 5, 10, 5, 12, 5, 195, 9, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 202, 8, 5, 1, 5, 1, 5, 1, 5, 3, 5, 207, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 5, 5, 215, 8, 5, 10, 5, 12, 5, 218, 9, 5, 1, 6, 1, 6, 3, 6, 222, 8, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 3, 6, 229, 8, 6, 1, 6, 1, 6, 1, 6, 3, 6, 234, 8, 6, 1, 7, 1, 7, 1, 7, 3, 7, 239, 8, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 3, 8, 249, 8, 8, 1, 9, 1, 9, 1, 9, 1, 9, 3, 9, 255, 8, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 5, 9, 263, 8, 9, 10, 9, 12, 9, 266, 9, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 3, 10, 276, 8, 10, 1, 10, 1, 10, 1, 10, 5, 10, 281, 8, 10, 10, 10, 12, 10, 284, 9, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 5, 11, 292, 8, 11, 10, 11, 12, 11, 295, 9, 11, 1, 11, 1, 11, 3, 11, 299, 8, 11, 3, 11, 301, 8, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 5, 13, 312, 8, 13, 10, 13, 12, 13, 315, 9, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 5, 17, 331, 8, 17, 10, 17, 12, 17, 334, 9, 17, 1, 18, 1, 18, 1, 18, 3, 18, 339, 8, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 5, 19, 347, 8, 19, 10, 19, 12, 19, 350, 9, 19, 1, 19, 3, 19, 353, 8, 19, 1, 20, 1, 20, 1, 20, 3, 20, 358, 8, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 23, 5, 23, 370, 8, 23, 10, 23, 12, 23, 373, 9, 23, 1, 24, 1, 24, 1, 24, 1, 24, 5, 24, 379, 8, 24, 10, 24, 12, 24, 382, 9, 24, 1, 24, 3, 24, 385, 8, 24, 1, 24, 1, 24, 3, 24, 389, 8, 24, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 3, 26, 396, 8, 26, 1, 26, 1, 26, 3, 26, 400, 8, 26, 1, 27, 1, 27, 1, 27, 5, 27, 405, 8, 27, 10, 27, 12, 27, 408, 9, 27, 1, 28, 1, 28, 1, 28, 3, 28, 413, 8, 28, 1, 29, 1, 29, 1, 29, 5, 29, 418, 8, 29, 10, 29, 12, 29, 421, 9, 29, 1, 30, 1, 30, 1, 30, 5, 30, 426, 8, 30, 10, 30, 12, 30, 429, 9, 30, 1, 31, 1, 31, 1, 31, 5, 31, 434, 8, 31, 10, 31, 12, 31, 437, 9, 31, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 3, 33, 444, 8, 33, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 5, 34, 459, 8, 34, 10, 34, 12, 34, 462, 9, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 5, 34, 470, 8, 34, 10, 34, 12, 34, 473, 9, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 5, 34, 481, 8, 34, 10, 34, 12, 34, 484, 9, 34, 1, 34, 1, 34, 3, 34, 488, 8, 34, 1, 35, 1, 35, 3, 35, 492, 8, 35, 1, 36, 1, 36, 1, 36, 3, 36, 497, 8, 36, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 38, 5, 38, 506, 8, 38, 10, 38, 12, 38, 509, 9, 38, 1, 39, 1, 39, 3, 39, 513, 8, 39, 1, 39, 1, 39, 3, 39, 517, 8, 39, 1, 40, 1, 40, 1, 40, 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, 42, 1, 42, 5, 42, 529, 8, 42, 10, 42, 12, 42, 532, 9, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 44, 1, 44, 1, 44, 1, 44, 3, 44, 542, 8, 44, 1, 45, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 5, 47, 554, 8, 47, 10, 47, 12, 47, 557, 9, 47, 1, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1, 50, 1, 50, 3, 50, 567, 8, 50, 1, 51, 3, 51, 570, 8, 51, 1, 51, 1, 51, 1, 52, 3, 52, 575, 8, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 56, 1, 56, 1, 56, 1, 56, 1, 57, 1, 57, 1, 57, 1, 58, 1, 58, 1, 58, 1, 58, 3, 58, 597, 8, 58, 1, 58, 1, 58, 1, 58, 1, 58, 5, 58, 603, 8, 58, 10, 58, 12, 58, 606, 9, 58, 3, 58, 608, 8, 58, 1, 59, 1, 59, 1, 59, 3, 59, 613, 8, 59, 1, 59, 1, 59, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 61, 1, 61, 1, 61, 1, 61, 3, 61, 626, 8, 61, 1, 62, 3, 62, 629, 8, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 63, 1, 63, 1, 63, 3, 63, 638, 8, 63, 1, 64, 1, 64, 1, 64, 1, 64, 5, 64, 644, 8, 64, 10, 64, 12, 64, 647, 9, 64, 1, 65, 1, 65, 1, 65, 0, 4, 2, 10, 18, 20, 66, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 0, 9, 1, 0, 64, 65, 1, 0, 66, 68, 2, 0, 30, 30, 83, 83, 1, 0, 74, 75, 2, 0, 35, 35, 40, 40, 2, 0, 43, 43, 46, 46, 2, 0, 42, 42, 56, 56, 2, 0, 57, 57, 59, 63, 1, 0, 22, 24, 678, 0, 132, 1, 0, 0, 0, 2, 135, 1, 0, 0, 0, 4, 152, 1, 0, 0, 0, 6, 172, 1, 0, 0, 0, 8, 174, 1, 0, 0, 0, 10, 206, 1, 0, 0, 0, 12, 233, 1, 0, 0, 0, 14, 235, 1, 0, 0, 0, 16, 248, 1, 0, 0, 0, 18, 254, 1, 0, 0, 0, 20, 275, 1, 0, 0, 0, 22, 285, 1, 0, 0, 0, 24, 304, 1, 0, 0, 0, 26, 306, 1, 0, 0, 0, 28, 318, 1, 0, 0, 0, 30, 322, 1, 0, 0, 0, 32, 324, 1, 0, 0, 0, 34, 327, 1, 0, 0, 0, 36, 338, 1, 0, 0, 0, 38, 342, 1, 0, 0, 0, 40, 357, 1, 0, 0, 0, 42, 361, 1, 0, 0, 0, 44, 363, 1, 0, 0, 0, 46, 365, 1, 0, 0, 0, 48, 374, 1, 0, 0, 0, 50, 390, 1, 0, 0, 0, 52, 393, 1, 0, 0, 0, 54, 401, 1, 0, 0, 0, 56, 409, 1, 0, 0, 0, 58, 414, 1, 0, 0, 0, 60, 422, 1, 0, 0, 0, 62, 430, 1, 0, 0, 0, 64, 438, 1, 0, 0, 0, 66, 443, 1, 0, 0, 0, 68, 487, 1, 0, 0, 0, 70, 491, 1, 0, 0, 0, 72, 496, 1, 0, 0, 0, 74, 498, 1, 0, 0, 0, 76, 501, 1, 0, 0, 0, 78, 510, 1, 0, 0, 0, 80, 518, 1, 0, 0, 0, 82, 521, 1, 0, 0, 0, 84, 524, 1, 0, 0, 0, 86, 533, 1, 0, 0, 0, 88, 537, 1, 0, 0, 0, 90, 543, 1, 0, 0, 0, 92, 547, 1, 0, 0, 0, 94, 550, 1, 0, 0, 0, 96, 558, 1, 0, 0, 0, 98, 562, 1, 0, 0, 0, 100, 566, 1, 0, 0, 0, 102, 569, 1, 0, 0, 0, 104, 574, 1, 0, 0, 0, 106, 578, 1, 0, 0, 0, 108, 580, 1, 0, 0, 0, 110, 582, 1, 0, 0, 0, 112, 585, 1, 0, 0, 0, 114, 589, 1, 0, 0, 0, 116, 592, 1, 0, 0, 0, 118, 612, 1, 0, 0, 0, 120, 616, 1, 0, 0, 0, 122, 621, 1, 0, 0, 0, 124, 628, 1, 0, 0, 0, 126, 634, 1, 0, 0, 0, 128, 639, 1, 0, 0, 0, 130, 648, 1, 0, 0, 0, 132, 133, 3, 2, 1, 0, 133, 134, 5, 0, 0, 1, 134, 1, 1, 0, 0, 0, 135, 136, 6, 1, -1, 0, 136, 137, 3, 4, 2, 0, 137, 143, 1, 0, 0, 0, 138, 139, 10, 1, 0, 0, 139, 140, 5, 29, 0, 0, 140, 142, 3, 6, 3, 0, 141, 138, 1, 0, 0, 0, 142, 145, 1, 0, 0, 0, 143, 141, 1, 0, 0, 0, 143, 144, 1, 0, 0, 0, 144, 3, 1, 0, 0, 0, 145, 143, 1, 0, 0, 0, 146, 153, 3, 110, 55, 0, 147, 153, 3, 38, 19, 0, 148, 153, 3, 32, 16, 0, 149, 153, 3, 114, 57, 0, 150, 151, 4, 2, 1, 0, 151, 153, 3, 48, 24, 0, 152, 146, 1, 0, 0, 0, 152, 147, 1, 0, 0, 0, 152, 148, 1, 0, 0, 0, 152, 149, 1, 0, 0, 0, 152, 150, 1, 0, 0, 0, 153, 5, 1, 0, 0, 0, 154, 173, 3, 50, 25, 0, 155, 173, 3, 8, 4, 0, 156, 173, 3, 80, 40, 0, 157, 173, 3, 74, 37, 0, 158, 173, 3, 52, 26, 0, 159, 173, 3, 76, 38, 0, 160, 173, 3, 82, 41, 0, 161, 173, 3, 84, 42, 0, 162, 173, 3, 88, 44, 0, 163, 173, 3, 90, 45, 0, 164, 173, 3, 116, 58, 0, 165, 173, 3, 92, 46, 0, 166, 167, 4, 3, 2, 0, 167, 173, 3, 122, 61, 0, 168, 169, 4, 3, 3, 0, 169, 173, 3, 120, 60, 0, 170, 171, 4, 3, 4, 0, 171, 173, 3, 124, 62, 0, 172, 154, 1, 0, 0, 0, 172, 155, 1, 0, 0, 0, 172, 156, 1, 0, 0, 0, 172, 157, 1, 0, 0, 0, 172, 158, 1, 0, 0, 0, 172, 159, 1, 0, 0, 0, 172, 160, 1, 0, 0, 0, 172, 161, 1, 0, 0, 0, 172, 162, 1, 0, 0, 0, 172, 163, 1, 0, 0, 0, 172, 164, 1, 0, 0, 0, 172, 165, 1, 0, 0, 0, 172, 166, 1, 0, 0, 0, 172, 168, 1, 0, 0, 0, 172, 170, 1, 0, 0, 0, 173, 7, 1, 0, 0, 0, 174, 175, 5, 16, 0, 0, 175, 176, 3, 10, 5, 0, 176, 9, 1, 0, 0, 0, 177, 178, 6, 5, -1, 0, 178, 179, 5, 49, 0, 0, 179, 207, 3, 10, 5, 8, 180, 207, 3, 16, 8, 0, 181, 207, 3, 12, 6, 0, 182, 184, 3, 16, 8, 0, 183, 185, 5, 49, 0, 0, 184, 183, 1, 0, 0, 0, 184, 185, 1, 0, 0, 0, 185, 186, 1, 0, 0, 0, 186, 187, 5, 44, 0, 0, 187, 188, 5, 48, 0, 0, 188, 193, 3, 16, 8, 0, 189, 190, 5, 39, 0, 0, 190, 192, 3, 16, 8, 0, 191, 189, 1, 0, 0, 0, 192, 195, 1, 0, 0, 0, 193, 191, 1, 0, 0, 0, 193, 194, 1, 0, 0, 0, 194, 196, 1, 0, 0, 0, 195, 193, 1, 0, 0, 0, 196, 197, 5, 55, 0, 0, 197, 207, 1, 0, 0, 0, 198, 199, 3, 16, 8, 0, 199, 201, 5, 45, 0, 0, 200, 202, 5, 49, 0, 0, 201, 200, 1, 0, 0, 0, 201, 202, 1, 0, 0, 0, 202, 203, 1, 0, 0, 0, 203, 204, 5, 50, 0, 0, 204, 207, 1, 0, 0, 0, 205, 207, 3, 14, 7, 0, 206, 177, 1, 0, 0, 0, 206, 180, 1, 0, 0, 0, 206, 181, 1, 0, 0, 0, 206, 182, 1, 0, 0, 0, 206, 198, 1, 0, 0, 0, 206, 205, 1, 0, 0, 0, 207, 216, 1, 0, 0, 0, 208, 209, 10, 5, 0, 0, 209, 210, 5, 34, 0, 0, 210, 215, 3, 10, 5, 6, 211, 212, 10, 4, 0, 0, 212, 213, 5, 52, 0, 0, 213, 215, 3, 10, 5, 5, 214, 208, 1, 0, 0, 0, 214, 211, 1, 0, 0, 0, 215, 218, 1, 0, 0, 0, 216, 214, 1, 0, 0, 0, 216, 217, 1, 0, 0, 0, 217, 11, 1, 0, 0, 0, 218, 216, 1, 0, 0, 0, 219, 221, 3, 16, 8, 0, 220, 222, 5, 49, 0, 0, 221, 220, 1, 0, 0, 0, 221, 222, 1, 0, 0, 0, 222, 223, 1, 0, 0, 0, 223, 224, 5, 47, 0, 0, 224, 225, 3, 106, 53, 0, 225, 234, 1, 0, 0, 0, 226, 228, 3, 16, 8, 0, 227, 229, 5, 49, 0, 0, 228, 227, 1, 0, 0, 0, 228, 229, 1, 0, 0, 0, 229, 230, 1, 0, 0, 0, 230, 231, 5, 54, 0, 0, 231, 232, 3, 106, 53, 0, 232, 234, 1, 0, 0, 0, 233, 219, 1, 0, 0, 0, 233, 226, 1, 0, 0, 0, 234, 13, 1, 0, 0, 0, 235, 238, 3, 58, 29, 0, 236, 237, 5, 37, 0, 0, 237, 239, 3, 30, 15, 0, 238, 236, 1, 0, 0, 0, 238, 239, 1, 0, 0, 0, 239, 240, 1, 0, 0, 0, 240, 241, 5, 38, 0, 0, 241, 242, 3, 68, 34, 0, 242, 15, 1, 0, 0, 0, 243, 249, 3, 18, 9, 0, 244, 245, 3, 18, 9, 0, 245, 246, 3, 108, 54, 0, 246, 247, 3, 18, 9, 0, 247, 249, 1, 0, 0, 0, 248, 243, 1, 0, 0, 0, 248, 244, 1, 0, 0, 0, 249, 17, 1, 0, 0, 0, 250, 251, 6, 9, -1, 0, 251, 255, 3, 20, 10, 0, 252, 253, 7, 0, 0, 0, 253, 255, 3, 18, 9, 3, 254, 250, 1, 0, 0, 0, 254, 252, 1, 0, 0, 0, 255, 264, 1, 0, 0, 0, 256, 257, 10, 2, 0, 0, 257, 258, 7, 1, 0, 0, 258, 263, 3, 18, 9, 3, 259, 260, 10, 1, 0, 0, 260, 261, 7, 0, 0, 0, 261, 263, 3, 18, 9, 2, 262, 256, 1, 0, 0, 0, 262, 259, 1, 0, 0, 0, 263, 266, 1, 0, 0, 0, 264, 262, 1, 0, 0, 0, 264, 265, 1, 0, 0, 0, 265, 19, 1, 0, 0, 0, 266, 264, 1, 0, 0, 0, 267, 268, 6, 10, -1, 0, 268, 276, 3, 68, 34, 0, 269, 276, 3, 58, 29, 0, 270, 276, 3, 22, 11, 0, 271, 272, 5, 48, 0, 0, 272, 273, 3, 10, 5, 0, 273, 274, 5, 55, 0, 0, 274, 276, 1, 0, 0, 0, 275, 267, 1, 0, 0, 0, 275, 269, 1, 0, 0, 0, 275, 270, 1, 0, 0, 0, 275, 271, 1, 0, 0, 0, 276, 282, 1, 0, 0, 0, 277, 278, 10, 1, 0, 0, 278, 279, 5, 37, 0, 0, 279, 281, 3, 30, 15, 0, 280, 277, 1, 0, 0, 0, 281, 284, 1, 0, 0, 0, 282, 280, 1, 0, 0, 0, 282, 283, 1, 0, 0, 0, 283, 21, 1, 0, 0, 0, 284, 282, 1, 0, 0, 0, 285, 286, 3, 24, 12, 0, 286, 300, 5, 48, 0, 0, 287, 301, 5, 66, 0, 0, 288, 293, 3, 10, 5, 0, 289, 290, 5, 39, 0, 0, 290, 292, 3, 10, 5, 0, 291, 289, 1, 0, 0, 0, 292, 295, 1, 0, 0, 0, 293, 291, 1, 0, 0, 0, 293, 294, 1, 0, 0, 0, 294, 298, 1, 0, 0, 0, 295, 293, 1, 0, 0, 0, 296, 297, 5, 39, 0, 0, 297, 299, 3, 26, 13, 0, 298, 296, 1, 0, 0, 0, 298, 299, 1, 0, 0, 0, 299, 301, 1, 0, 0, 0, 300, 287, 1, 0, 0, 0, 300, 288, 1, 0, 0, 0, 300, 301, 1, 0, 0, 0, 301, 302, 1, 0, 0, 0, 302, 303, 5, 55, 0, 0, 303, 23, 1, 0, 0, 0, 304, 305, 3, 72, 36, 0, 305, 25, 1, 0, 0, 0, 306, 307, 4, 13, 10, 0, 307, 308, 5, 69, 0, 0, 308, 313, 3, 28, 14, 0, 309, 310, 5, 39, 0, 0, 310, 312, 3, 28, 14, 0, 311, 309, 1, 0, 0, 0, 312, 315, 1, 0, 0, 0, 313, 311, 1, 0, 0, 0, 313, 314, 1, 0, 0, 0, 314, 316, 1, 0, 0, 0, 315, 313, 1, 0, 0, 0, 316, 317, 5, 70, 0, 0, 317, 27, 1, 0, 0, 0, 318, 319, 3, 106, 53, 0, 319, 320, 5, 38, 0, 0, 320, 321, 3, 68, 34, 0, 321, 29, 1, 0, 0, 0, 322, 323, 3, 64, 32, 0, 323, 31, 1, 0, 0, 0, 324, 325, 5, 12, 0, 0, 325, 326, 3, 34, 17, 0, 326, 33, 1, 0, 0, 0, 327, 332, 3, 36, 18, 0, 328, 329, 5, 39, 0, 0, 329, 331, 3, 36, 18, 0, 330, 328, 1, 0, 0, 0, 331, 334, 1, 0, 0, 0, 332, 330, 1, 0, 0, 0, 332, 333, 1, 0, 0, 0, 333, 35, 1, 0, 0, 0, 334, 332, 1, 0, 0, 0, 335, 336, 3, 58, 29, 0, 336, 337, 5, 36, 0, 0, 337, 339, 1, 0, 0, 0, 338, 335, 1, 0, 0, 0, 338, 339, 1, 0, 0, 0, 339, 340, 1, 0, 0, 0, 340, 341, 3, 10, 5, 0, 341, 37, 1, 0, 0, 0, 342, 343, 5, 6, 0, 0, 343, 348, 3, 40, 20, 0, 344, 345, 5, 39, 0, 0, 345, 347, 3, 40, 20, 0, 346, 344, 1, 0, 0, 0, 347, 350, 1, 0, 0, 0, 348, 346, 1, 0, 0, 0, 348, 349, 1, 0, 0, 0, 349, 352, 1, 0, 0, 0, 350, 348, 1, 0, 0, 0, 351, 353, 3, 46, 23, 0, 352, 351, 1, 0, 0, 0, 352, 353, 1, 0, 0, 0, 353, 39, 1, 0, 0, 0, 354, 355, 3, 42, 21, 0, 355, 356, 5, 38, 0, 0, 356, 358, 1, 0, 0, 0, 357, 354, 1, 0, 0, 0, 357, 358, 1, 0, 0, 0, 358, 359, 1, 0, 0, 0, 359, 360, 3, 44, 22, 0, 360, 41, 1, 0, 0, 0, 361, 362, 5, 83, 0, 0, 362, 43, 1, 0, 0, 0, 363, 364, 7, 2, 0, 0, 364, 45, 1, 0, 0, 0, 365, 366, 5, 82, 0, 0, 366, 371, 5, 83, 0, 0, 367, 368, 5, 39, 0, 0, 368, 370, 5, 83, 0, 0, 369, 367, 1, 0, 0, 0, 370, 373, 1, 0, 0, 0, 371, 369, 1, 0, 0, 0, 371, 372, 1, 0, 0, 0, 372, 47, 1, 0, 0, 0, 373, 371, 1, 0, 0, 0, 374, 375, 5, 19, 0, 0, 375, 380, 3, 40, 20, 0, 376, 377, 5, 39, 0, 0, 377, 379, 3, 40, 20, 0, 378, 376, 1, 0, 0, 0, 379, 382, 1, 0, 0, 0, 380, 378, 1, 0, 0, 0, 380, 381, 1, 0, 0, 0, 381, 384, 1, 0, 0, 0, 382, 380, 1, 0, 0, 0, 383, 385, 3, 54, 27, 0, 384, 383, 1, 0, 0, 0, 384, 385, 1, 0, 0, 0, 385, 388, 1, 0, 0, 0, 386, 387, 5, 33, 0, 0, 387, 389, 3, 34, 17, 0, 388, 386, 1, 0, 0, 0, 388, 389, 1, 0, 0, 0, 389, 49, 1, 0, 0, 0, 390, 391, 5, 4, 0, 0, 391, 392, 3, 34, 17, 0, 392, 51, 1, 0, 0, 0, 393, 395, 5, 15, 0, 0, 394, 396, 3, 54, 27, 0, 395, 394, 1, 0, 0, 0, 395, 396, 1, 0, 0, 0, 396, 399, 1, 0, 0, 0, 397, 398, 5, 33, 0, 0, 398, 400, 3, 34, 17, 0, 399, 397, 1, 0, 0, 0, 399, 400, 1, 0, 0, 0, 400, 53, 1, 0, 0, 0, 401, 406, 3, 56, 28, 0, 402, 403, 5, 39, 0, 0, 403, 405, 3, 56, 28, 0, 404, 402, 1, 0, 0, 0, 405, 408, 1, 0, 0, 0, 406, 404, 1, 0, 0, 0, 406, 407, 1, 0, 0, 0, 407, 55, 1, 0, 0, 0, 408, 406, 1, 0, 0, 0, 409, 412, 3, 36, 18, 0, 410, 411, 5, 16, 0, 0, 411, 413, 3, 10, 5, 0, 412, 410, 1, 0, 0, 0, 412, 413, 1, 0, 0, 0, 413, 57, 1, 0, 0, 0, 414, 419, 3, 72, 36, 0, 415, 416, 5, 41, 0, 0, 416, 418, 3, 72, 36, 0, 417, 415, 1, 0, 0, 0, 418, 421, 1, 0, 0, 0, 419, 417, 1, 0, 0, 0, 419, 420, 1, 0, 0, 0, 420, 59, 1, 0, 0, 0, 421, 419, 1, 0, 0, 0, 422, 427, 3, 66, 33, 0, 423, 424, 5, 41, 0, 0, 424, 426, 3, 66, 33, 0, 425, 423, 1, 0, 0, 0, 426, 429, 1, 0, 0, 0, 427, 425, 1, 0, 0, 0, 427, 428, 1, 0, 0, 0, 428, 61, 1, 0, 0, 0, 429, 427, 1, 0, 0, 0, 430, 435, 3, 60, 30, 0, 431, 432, 5, 39, 0, 0, 432, 434, 3, 60, 30, 0, 433, 431, 1, 0, 0, 0, 434, 437, 1, 0, 0, 0, 435, 433, 1, 0, 0, 0, 435, 436, 1, 0, 0, 0, 436, 63, 1, 0, 0, 0, 437, 435, 1, 0, 0, 0, 438, 439, 7, 3, 0, 0, 439, 65, 1, 0, 0, 0, 440, 444, 5, 87, 0, 0, 441, 442, 4, 33, 11, 0, 442, 444, 3, 70, 35, 0, 443, 440, 1, 0, 0, 0, 443, 441, 1, 0, 0, 0, 444, 67, 1, 0, 0, 0, 445, 488, 5, 50, 0, 0, 446, 447, 3, 104, 52, 0, 447, 448, 5, 74, 0, 0, 448, 488, 1, 0, 0, 0, 449, 488, 3, 102, 51, 0, 450, 488, 3, 104, 52, 0, 451, 488, 3, 98, 49, 0, 452, 488, 3, 70, 35, 0, 453, 488, 3, 106, 53, 0, 454, 455, 5, 72, 0, 0, 455, 460, 3, 100, 50, 0, 456, 457, 5, 39, 0, 0, 457, 459, 3, 100, 50, 0, 458, 456, 1, 0, 0, 0, 459, 462, 1, 0, 0, 0, 460, 458, 1, 0, 0, 0, 460, 461, 1, 0, 0, 0, 461, 463, 1, 0, 0, 0, 462, 460, 1, 0, 0, 0, 463, 464, 5, 73, 0, 0, 464, 488, 1, 0, 0, 0, 465, 466, 5, 72, 0, 0, 466, 471, 3, 98, 49, 0, 467, 468, 5, 39, 0, 0, 468, 470, 3, 98, 49, 0, 469, 467, 1, 0, 0, 0, 470, 473, 1, 0, 0, 0, 471, 469, 1, 0, 0, 0, 471, 472, 1, 0, 0, 0, 472, 474, 1, 0, 0, 0, 473, 471, 1, 0, 0, 0, 474, 475, 5, 73, 0, 0, 475, 488, 1, 0, 0, 0, 476, 477, 5, 72, 0, 0, 477, 482, 3, 106, 53, 0, 478, 479, 5, 39, 0, 0, 479, 481, 3, 106, 53, 0, 480, 478, 1, 0, 0, 0, 481, 484, 1, 0, 0, 0, 482, 480, 1, 0, 0, 0, 482, 483, 1, 0, 0, 0, 483, 485, 1, 0, 0, 0, 484, 482, 1, 0, 0, 0, 485, 486, 5, 73, 0, 0, 486, 488, 1, 0, 0, 0, 487, 445, 1, 0, 0, 0, 487, 446, 1, 0, 0, 0, 487, 449, 1, 0, 0, 0, 487, 450, 1, 0, 0, 0, 487, 451, 1, 0, 0, 0, 487, 452, 1, 0, 0, 0, 487, 453, 1, 0, 0, 0, 487, 454, 1, 0, 0, 0, 487, 465, 1, 0, 0, 0, 487, 476, 1, 0, 0, 0, 488, 69, 1, 0, 0, 0, 489, 492, 5, 53, 0, 0, 490, 492, 5, 71, 0, 0, 491, 489, 1, 0, 0, 0, 491, 490, 1, 0, 0, 0, 492, 71, 1, 0, 0, 0, 493, 497, 3, 64, 32, 0, 494, 495, 4, 36, 12, 0, 495, 497, 3, 70, 35, 0, 496, 493, 1, 0, 0, 0, 496, 494, 1, 0, 0, 0, 497, 73, 1, 0, 0, 0, 498, 499, 5, 9, 0, 0, 499, 500, 5, 31, 0, 0, 500, 75, 1, 0, 0, 0, 501, 502, 5, 14, 0, 0, 502, 507, 3, 78, 39, 0, 503, 504, 5, 39, 0, 0, 504, 506, 3, 78, 39, 0, 505, 503, 1, 0, 0, 0, 506, 509, 1, 0, 0, 0, 507, 505, 1, 0, 0, 0, 507, 508, 1, 0, 0, 0, 508, 77, 1, 0, 0, 0, 509, 507, 1, 0, 0, 0, 510, 512, 3, 10, 5, 0, 511, 513, 7, 4, 0, 0, 512, 511, 1, 0, 0, 0, 512, 513, 1, 0, 0, 0, 513, 516, 1, 0, 0, 0, 514, 515, 5, 51, 0, 0, 515, 517, 7, 5, 0, 0, 516, 514, 1, 0, 0, 0, 516, 517, 1, 0, 0, 0, 517, 79, 1, 0, 0, 0, 518, 519, 5, 8, 0, 0, 519, 520, 3, 62, 31, 0, 520, 81, 1, 0, 0, 0, 521, 522, 5, 2, 0, 0, 522, 523, 3, 62, 31, 0, 523, 83, 1, 0, 0, 0, 524, 525, 5, 11, 0, 0, 525, 530, 3, 86, 43, 0, 526, 527, 5, 39, 0, 0, 527, 529, 3, 86, 43, 0, 528, 526, 1, 0, 0, 0, 529, 532, 1, 0, 0, 0, 530, 528, 1, 0, 0, 0, 530, 531, 1, 0, 0, 0, 531, 85, 1, 0, 0, 0, 532, 530, 1, 0, 0, 0, 533, 534, 3, 60, 30, 0, 534, 535, 5, 91, 0, 0, 535, 536, 3, 60, 30, 0, 536, 87, 1, 0, 0, 0, 537, 538, 5, 1, 0, 0, 538, 539, 3, 20, 10, 0, 539, 541, 3, 106, 53, 0, 540, 542, 3, 94, 47, 0, 541, 540, 1, 0, 0, 0, 541, 542, 1, 0, 0, 0, 542, 89, 1, 0, 0, 0, 543, 544, 5, 7, 0, 0, 544, 545, 3, 20, 10, 0, 545, 546, 3, 106, 53, 0, 546, 91, 1, 0, 0, 0, 547, 548, 5, 10, 0, 0, 548, 549, 3, 58, 29, 0, 549, 93, 1, 0, 0, 0, 550, 555, 3, 96, 48, 0, 551, 552, 5, 39, 0, 0, 552, 554, 3, 96, 48, 0, 553, 551, 1, 0, 0, 0, 554, 557, 1, 0, 0, 0, 555, 553, 1, 0, 0, 0, 555, 556, 1, 0, 0, 0, 556, 95, 1, 0, 0, 0, 557, 555, 1, 0, 0, 0, 558, 559, 3, 64, 32, 0, 559, 560, 5, 36, 0, 0, 560, 561, 3, 68, 34, 0, 561, 97, 1, 0, 0, 0, 562, 563, 7, 6, 0, 0, 563, 99, 1, 0, 0, 0, 564, 567, 3, 102, 51, 0, 565, 567, 3, 104, 52, 0, 566, 564, 1, 0, 0, 0, 566, 565, 1, 0, 0, 0, 567, 101, 1, 0, 0, 0, 568, 570, 7, 0, 0, 0, 569, 568, 1, 0, 0, 0, 569, 570, 1, 0, 0, 0, 570, 571, 1, 0, 0, 0, 571, 572, 5, 32, 0, 0, 572, 103, 1, 0, 0, 0, 573, 575, 7, 0, 0, 0, 574, 573, 1, 0, 0, 0, 574, 575, 1, 0, 0, 0, 575, 576, 1, 0, 0, 0, 576, 577, 5, 31, 0, 0, 577, 105, 1, 0, 0, 0, 578, 579, 5, 30, 0, 0, 579, 107, 1, 0, 0, 0, 580, 581, 7, 7, 0, 0, 581, 109, 1, 0, 0, 0, 582, 583, 5, 5, 0, 0, 583, 584, 3, 112, 56, 0, 584, 111, 1, 0, 0, 0, 585, 586, 5, 72, 0, 0, 586, 587, 3, 2, 1, 0, 587, 588, 5, 73, 0, 0, 588, 113, 1, 0, 0, 0, 589, 590, 5, 13, 0, 0, 590, 591, 5, 107, 0, 0, 591, 115, 1, 0, 0, 0, 592, 593, 5, 3, 0, 0, 593, 596, 5, 97, 0, 0, 594, 595, 5, 95, 0, 0, 595, 597, 3, 60, 30, 0, 596, 594, 1, 0, 0, 0, 596, 597, 1, 0, 0, 0, 597, 607, 1, 0, 0, 0, 598, 599, 5, 96, 0, 0, 599, 604, 3, 118, 59, 0, 600, 601, 5, 39, 0, 0, 601, 603, 3, 118, 59, 0, 602, 600, 1, 0, 0, 0, 603, 606, 1, 0, 0, 0, 604, 602, 1, 0, 0, 0, 604, 605, 1, 0, 0, 0, 605, 608, 1, 0, 0, 0, 606, 604, 1, 0, 0, 0, 607, 598, 1, 0, 0, 0, 607, 608, 1, 0, 0, 0, 608, 117, 1, 0, 0, 0, 609, 610, 3, 60, 30, 0, 610, 611, 5, 36, 0, 0, 611, 613, 1, 0, 0, 0, 612, 609, 1, 0, 0, 0, 612, 613, 1, 0, 0, 0, 613, 614, 1, 0, 0, 0, 614, 615, 3, 60, 30, 0, 615, 119, 1, 0, 0, 0, 616, 617, 5, 18, 0, 0, 617, 618, 3, 40, 20, 0, 618, 619, 5, 95, 0, 0, 619, 620, 3, 62, 31, 0, 620, 121, 1, 0, 0, 0, 621, 622, 5, 17, 0, 0, 622, 625, 3, 54, 27, 0, 623, 624, 5, 33, 0, 0, 624, 626, 3, 34, 17, 0, 625, 623, 1, 0, 0, 0, 625, 626, 1, 0, 0, 0, 626, 123, 1, 0, 0, 0, 627, 629, 7, 8, 0, 0, 628, 627, 1, 0, 0, 0, 628, 629, 1, 0, 0, 0, 629, 630, 1, 0, 0, 0, 630, 631, 5, 20, 0, 0, 631, 632, 3, 126, 63, 0, 632, 633, 3, 128, 64, 0, 633, 125, 1, 0, 0, 0, 634, 637, 3, 40, 20, 0, 635, 636, 5, 91, 0, 0, 636, 638, 3, 64, 32, 0, 637, 635, 1, 0, 0, 0, 637, 638, 1, 0, 0, 0, 638, 127, 1, 0, 0, 0, 639, 640, 5, 95, 0, 0, 640, 645, 3, 130, 65, 0, 641, 642, 5, 39, 0, 0, 642, 644, 3, 130, 65, 0, 643, 641, 1, 0, 0, 0, 644, 647, 1, 0, 0, 0, 645, 643, 1, 0, 0, 0, 645, 646, 1, 0, 0, 0, 646, 129, 1, 0, 0, 0, 647, 645, 1, 0, 0, 0, 648, 649, 3, 16, 8, 0, 649, 131, 1, 0, 0, 0, 63, 143, 152, 172, 184, 193, 201, 206, 214, 216, 221, 228, 233, 238, 248, 254, 262, 264, 275, 282, 293, 298, 300, 313, 332, 338, 348, 352, 357, 371, 380, 384, 388, 395, 399, 406, 412, 419, 427, 435, 443, 460, 471, 482, 487, 491, 496, 507, 512, 516, 530, 541, 555, 566, 569, 574, 596, 604, 607, 612, 625, 628, 637, 645] \ No newline at end of file diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParser.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParser.java index beb14e1588472..35ace5a34f73f 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParser.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParser.java @@ -5540,15 +5540,15 @@ public final JoinCommandContext joinCommand() throws RecognitionException { @SuppressWarnings("CheckReturnValue") public static class JoinTargetContext extends ParserRuleContext { - public IdentifierContext index; + public IndexPatternContext index; public IdentifierContext alias; - public List identifier() { - return getRuleContexts(IdentifierContext.class); - } - public IdentifierContext identifier(int i) { - return getRuleContext(IdentifierContext.class,i); + public IndexPatternContext indexPattern() { + return getRuleContext(IndexPatternContext.class,0); } public TerminalNode AS() { return getToken(EsqlBaseParser.AS, 0); } + public IdentifierContext identifier() { + return getRuleContext(IdentifierContext.class,0); + } @SuppressWarnings("this-escape") public JoinTargetContext(ParserRuleContext parent, int invokingState) { super(parent, invokingState); @@ -5577,7 +5577,7 @@ public final JoinTargetContext joinTarget() throws RecognitionException { enterOuterAlt(_localctx, 1); { setState(634); - ((JoinTargetContext)_localctx).index = identifier(); + ((JoinTargetContext)_localctx).index = indexPattern(); setState(637); _errHandler.sync(this); _la = _input.LA(1); @@ -6216,7 +6216,7 @@ private boolean identifierOrParameter_sempred(IdentifierOrParameterContext _loca "\u0000\u0000\u0274\u0273\u0001\u0000\u0000\u0000\u0274\u0275\u0001\u0000"+ "\u0000\u0000\u0275\u0276\u0001\u0000\u0000\u0000\u0276\u0277\u0005\u0014"+ "\u0000\u0000\u0277\u0278\u0003~?\u0000\u0278\u0279\u0003\u0080@\u0000"+ - "\u0279}\u0001\u0000\u0000\u0000\u027a\u027d\u0003@ \u0000\u027b\u027c"+ + "\u0279}\u0001\u0000\u0000\u0000\u027a\u027d\u0003(\u0014\u0000\u027b\u027c"+ "\u0005[\u0000\u0000\u027c\u027e\u0003@ \u0000\u027d\u027b\u0001\u0000"+ "\u0000\u0000\u027d\u027e\u0001\u0000\u0000\u0000\u027e\u007f\u0001\u0000"+ "\u0000\u0000\u027f\u0280\u0005_\u0000\u0000\u0280\u0285\u0003\u0082A\u0000"+ diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java index cc1ac6ac31385..ba74bf467f2aa 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java @@ -34,7 +34,7 @@ import org.elasticsearch.xpack.esql.expression.Order; import org.elasticsearch.xpack.esql.expression.UnresolvedNamePattern; import org.elasticsearch.xpack.esql.expression.function.UnresolvedFunction; -import org.elasticsearch.xpack.esql.plan.TableIdentifier; +import org.elasticsearch.xpack.esql.plan.IndexPattern; import org.elasticsearch.xpack.esql.plan.logical.Aggregate; import org.elasticsearch.xpack.esql.plan.logical.Dissect; import org.elasticsearch.xpack.esql.plan.logical.Drop; @@ -255,7 +255,7 @@ public LogicalPlan visitRowCommand(EsqlBaseParser.RowCommandContext ctx) { @Override public LogicalPlan visitFromCommand(EsqlBaseParser.FromCommandContext ctx) { Source source = source(ctx); - TableIdentifier table = new TableIdentifier(source, null, visitIndexPattern(ctx.indexPattern())); + IndexPattern table = new IndexPattern(source, visitIndexPattern(ctx.indexPattern())); Map metadataMap = new LinkedHashMap<>(); if (ctx.metadata() != null) { for (var c : ctx.metadata().UNQUOTED_SOURCE()) { @@ -468,7 +468,7 @@ public LogicalPlan visitMetricsCommand(EsqlBaseParser.MetricsCommandContext ctx) throw new IllegalArgumentException("METRICS command currently requires a snapshot build"); } Source source = source(ctx); - TableIdentifier table = new TableIdentifier(source, null, visitIndexPattern(ctx.indexPattern())); + IndexPattern table = new IndexPattern(source, visitIndexPattern(ctx.indexPattern())); if (ctx.aggregates == null && ctx.grouping == null) { return new UnresolvedRelation(source, table, false, List.of(), IndexMode.STANDARD, null, "METRICS"); @@ -523,9 +523,14 @@ public PlanFactory visitJoinCommand(EsqlBaseParser.JoinCommandContext ctx) { } var target = ctx.joinTarget(); + var rightPattern = visitIndexPattern(List.of(target.index)); + if (rightPattern.contains(WILDCARD)) { + throw new ParsingException(source(target), "invalid index pattern [{}], * is not allowed in LOOKUP JOIN", rightPattern); + } + UnresolvedRelation right = new UnresolvedRelation( source(target), - new TableIdentifier(source(target.index), null, visitIdentifier(target.index)), + new IndexPattern(source(target.index), rightPattern), false, emptyList(), IndexMode.LOOKUP, diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/IndexPattern.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/IndexPattern.java new file mode 100644 index 0000000000000..fdaac1c1cc64c --- /dev/null +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/IndexPattern.java @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +package org.elasticsearch.xpack.esql.plan; + +import org.elasticsearch.xpack.esql.core.tree.Source; + +import java.util.Objects; + +/** + * Contains an index pattern together with its {@link Source}. Can also be a comma-separated list, like {@code idx-*,remote:other-idx*}. + */ +public class IndexPattern { + + private final Source source; + private final String indexPattern; + + public IndexPattern(Source source, String indexPattern) { + this.source = source; + this.indexPattern = indexPattern; + } + + public String indexPattern() { + return indexPattern; + } + + @Override + public int hashCode() { + return Objects.hash(indexPattern); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj == null || getClass() != obj.getClass()) { + return false; + } + + IndexPattern other = (IndexPattern) obj; + return Objects.equals(indexPattern, other.indexPattern); + } + + public Source source() { + return source; + } + + @Override + public String toString() { + return indexPattern; + } +} diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/TableIdentifier.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/TableIdentifier.java deleted file mode 100644 index 532d93eec48af..0000000000000 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/TableIdentifier.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -package org.elasticsearch.xpack.esql.plan; - -import org.elasticsearch.xpack.esql.core.tree.Source; - -import java.util.Objects; - -import static org.elasticsearch.transport.RemoteClusterAware.REMOTE_CLUSTER_INDEX_SEPARATOR; - -public class TableIdentifier { - - private final Source source; - - private final String cluster; - private final String index; - - public TableIdentifier(Source source, String catalog, String index) { - this.source = source; - this.cluster = catalog; - this.index = index; - } - - public String cluster() { - return cluster; - } - - public String index() { - return index; - } - - @Override - public int hashCode() { - return Objects.hash(cluster, index); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - - if (obj == null || getClass() != obj.getClass()) { - return false; - } - - TableIdentifier other = (TableIdentifier) obj; - return Objects.equals(index, other.index) && Objects.equals(cluster, other.cluster); - } - - public Source source() { - return source; - } - - public String qualifiedIndex() { - return cluster != null ? cluster + REMOTE_CLUSTER_INDEX_SEPARATOR + index : index; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - if (cluster != null) { - builder.append(cluster); - builder.append(REMOTE_CLUSTER_INDEX_SEPARATOR); - } - builder.append(index); - return builder.toString(); - } -} diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/UnresolvedRelation.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/UnresolvedRelation.java index 384c3f7a340ae..0a20e1dd9080d 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/UnresolvedRelation.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/UnresolvedRelation.java @@ -12,7 +12,7 @@ import org.elasticsearch.xpack.esql.core.expression.Attribute; import org.elasticsearch.xpack.esql.core.tree.NodeInfo; import org.elasticsearch.xpack.esql.core.tree.Source; -import org.elasticsearch.xpack.esql.plan.TableIdentifier; +import org.elasticsearch.xpack.esql.plan.IndexPattern; import java.util.Collections; import java.util.List; @@ -22,7 +22,7 @@ public class UnresolvedRelation extends LeafPlan implements Unresolvable { - private final TableIdentifier table; + private final IndexPattern indexPattern; private final boolean frozen; private final List metadataFields; /* @@ -40,7 +40,7 @@ public class UnresolvedRelation extends LeafPlan implements Unresolvable { public UnresolvedRelation( Source source, - TableIdentifier table, + IndexPattern indexPattern, boolean frozen, List metadataFields, IndexMode indexMode, @@ -48,11 +48,11 @@ public UnresolvedRelation( String commandName ) { super(source); - this.table = table; + this.indexPattern = indexPattern; this.frozen = frozen; this.metadataFields = metadataFields; this.indexMode = indexMode; - this.unresolvedMsg = unresolvedMessage == null ? "Unknown index [" + table.index() + "]" : unresolvedMessage; + this.unresolvedMsg = unresolvedMessage == null ? "Unknown index [" + indexPattern.indexPattern() + "]" : unresolvedMessage; this.commandName = commandName; } @@ -68,11 +68,11 @@ public String getWriteableName() { @Override protected NodeInfo info() { - return NodeInfo.create(this, UnresolvedRelation::new, table, frozen, metadataFields, indexMode, unresolvedMsg, commandName); + return NodeInfo.create(this, UnresolvedRelation::new, indexPattern, frozen, metadataFields, indexMode, unresolvedMsg, commandName); } - public TableIdentifier table() { - return table; + public IndexPattern indexPattern() { + return indexPattern; } public boolean frozen() { @@ -124,7 +124,7 @@ public String unresolvedMessage() { @Override public int hashCode() { - return Objects.hash(source(), table, metadataFields, indexMode, unresolvedMsg); + return Objects.hash(source(), indexPattern, metadataFields, indexMode, unresolvedMsg); } @Override @@ -138,7 +138,7 @@ public boolean equals(Object obj) { } UnresolvedRelation other = (UnresolvedRelation) obj; - return Objects.equals(table, other.table) + return Objects.equals(indexPattern, other.indexPattern) && Objects.equals(frozen, other.frozen) && Objects.equals(metadataFields, other.metadataFields) && indexMode == other.indexMode @@ -147,11 +147,11 @@ public boolean equals(Object obj) { @Override public List nodeProperties() { - return singletonList(table); + return singletonList(indexPattern); } @Override public String toString() { - return UNRESOLVED_PREFIX + table.index(); + return UNRESOLVED_PREFIX + indexPattern.indexPattern(); } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/LookupJoin.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/LookupJoin.java index 4e009156072df..c29cf0ec7f414 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/LookupJoin.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/LookupJoin.java @@ -7,9 +7,13 @@ package org.elasticsearch.xpack.esql.plan.logical.join; +import org.elasticsearch.index.IndexMode; +import org.elasticsearch.xpack.esql.capabilities.PostAnalysisVerificationAware; +import org.elasticsearch.xpack.esql.common.Failures; import org.elasticsearch.xpack.esql.core.expression.Attribute; import org.elasticsearch.xpack.esql.core.tree.NodeInfo; import org.elasticsearch.xpack.esql.core.tree.Source; +import org.elasticsearch.xpack.esql.plan.logical.EsRelation; import org.elasticsearch.xpack.esql.plan.logical.LogicalPlan; import org.elasticsearch.xpack.esql.plan.logical.SurrogateLogicalPlan; import org.elasticsearch.xpack.esql.plan.logical.join.JoinTypes.UsingJoinType; @@ -17,12 +21,13 @@ import java.util.List; import static java.util.Collections.emptyList; +import static org.elasticsearch.xpack.esql.common.Failure.fail; import static org.elasticsearch.xpack.esql.plan.logical.join.JoinTypes.LEFT; /** * Lookup join - specialized LEFT (OUTER) JOIN between the main left side and a lookup index (index_mode = lookup) on the right. */ -public class LookupJoin extends Join implements SurrogateLogicalPlan { +public class LookupJoin extends Join implements SurrogateLogicalPlan, PostAnalysisVerificationAware { public LookupJoin(Source source, LogicalPlan left, LogicalPlan right, List joinFields) { this(source, left, right, new UsingJoinType(LEFT, joinFields), emptyList(), emptyList(), emptyList()); @@ -71,4 +76,31 @@ protected NodeInfo info() { config().rightFields() ); } + + @Override + public void postAnalysisVerification(Failures failures) { + super.postAnalysisVerification(failures); + right().forEachDown(EsRelation.class, esr -> { + var indexNameWithModes = esr.indexNameWithModes(); + if (indexNameWithModes.size() != 1) { + failures.add( + fail(esr, "invalid [{}] resolution in lookup mode to [{}] indices", esr.indexPattern(), indexNameWithModes.size()) + ); + } else if (indexNameWithModes.values().iterator().next() != IndexMode.LOOKUP) { + failures.add( + fail( + esr, + "invalid [{}] resolution in lookup mode to an index in [{}] mode", + esr.indexPattern(), + indexNameWithModes.values().iterator().next() + ) + ); + } + + // this check is crucial for security: ES|QL would use the concrete indices, so it would bypass the security on the alias + if (esr.concreteIndices().contains(esr.indexPattern()) == false) { + failures.add(fail(this, "Aliases and index patterns are not allowed for LOOKUP JOIN [{}]", esr.indexPattern())); + } + }); + } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/EsqlSession.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/EsqlSession.java index b10f766babb36..b40e49df49c84 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/EsqlSession.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/EsqlSession.java @@ -56,7 +56,7 @@ import org.elasticsearch.xpack.esql.optimizer.PhysicalPlanOptimizer; import org.elasticsearch.xpack.esql.parser.EsqlParser; import org.elasticsearch.xpack.esql.parser.QueryParams; -import org.elasticsearch.xpack.esql.plan.TableIdentifier; +import org.elasticsearch.xpack.esql.plan.IndexPattern; import org.elasticsearch.xpack.esql.plan.logical.Aggregate; import org.elasticsearch.xpack.esql.plan.logical.Enrich; import org.elasticsearch.xpack.esql.plan.logical.Keep; @@ -321,7 +321,9 @@ public void analyzedPlan( EsqlSessionCCSUtils.checkForCcsLicense(executionInfo, indices, indicesExpressionGrouper, verifier.licenseState()); final Set targetClusters = enrichPolicyResolver.groupIndicesPerCluster( - indices.stream().flatMap(t -> Arrays.stream(Strings.commaDelimitedListToStringArray(t.id().index()))).toArray(String[]::new) + indices.stream() + .flatMap(t -> Arrays.stream(Strings.commaDelimitedListToStringArray(t.id().indexPattern()))) + .toArray(String[]::new) ).keySet(); var listener = SubscribableListener.newForked( @@ -373,14 +375,14 @@ public void analyzedPlan( } private void preAnalyzeLookupIndex(TableInfo tableInfo, PreAnalysisResult result, ActionListener listener) { - TableIdentifier table = tableInfo.id(); - Set fieldNames = result.wildcardJoinIndices().contains(table.index()) ? IndexResolver.ALL_FIELDS : result.fieldNames; + IndexPattern table = tableInfo.id(); + Set fieldNames = result.wildcardJoinIndices().contains(table.indexPattern()) ? IndexResolver.ALL_FIELDS : result.fieldNames; // call the EsqlResolveFieldsAction (field-caps) to resolve indices and get field types indexResolver.resolveAsMergedMapping( - table.index(), + table.indexPattern(), fieldNames, null, - listener.map(indexResolution -> result.addLookupIndexResolution(table.index(), indexResolution)) + listener.map(indexResolution -> result.addLookupIndexResolution(table.indexPattern(), indexResolution)) ); // TODO: Verify that the resolved index actually has indexMode: "lookup" } @@ -400,9 +402,12 @@ private void preAnalyzeIndices( // known to be unavailable from the enrich policy API call Map unavailableClusters = result.enrichResolution.getUnavailableClusters(); TableInfo tableInfo = indices.get(0); - TableIdentifier table = tableInfo.id(); + IndexPattern table = tableInfo.id(); - Map clusterIndices = indicesExpressionGrouper.groupIndices(IndicesOptions.DEFAULT, table.index()); + Map clusterIndices = indicesExpressionGrouper.groupIndices( + IndicesOptions.DEFAULT, + table.indexPattern() + ); for (Map.Entry entry : clusterIndices.entrySet()) { final String clusterAlias = entry.getKey(); String indexExpr = Strings.arrayToCommaDelimitedString(entry.getValue().indices()); @@ -431,7 +436,9 @@ private void preAnalyzeIndices( String indexExpressionToResolve = EsqlSessionCCSUtils.createIndexExpressionFromAvailableClusters(executionInfo); if (indexExpressionToResolve.isEmpty()) { // if this was a pure remote CCS request (no local indices) and all remotes are offline, return an empty IndexResolution - listener.onResponse(result.withIndexResolution(IndexResolution.valid(new EsIndex(table.index(), Map.of(), Map.of())))); + listener.onResponse( + result.withIndexResolution(IndexResolution.valid(new EsIndex(table.indexPattern(), Map.of(), Map.of()))) + ); } else { // call the EsqlResolveFieldsAction (field-caps) to resolve indices and get field types indexResolver.resolveAsMergedMapping( @@ -588,7 +595,7 @@ static PreAnalysisResult fieldNames(LogicalPlan parsed, Set enrichPolicy } if (keepCommandReferences.isEmpty()) { // No KEEP commands after the JOIN, so we need to mark this index for "*" field resolution - wildcardJoinIndices.add(((UnresolvedRelation) join.right()).table().index()); + wildcardJoinIndices.add(((UnresolvedRelation) join.right()).indexPattern().indexPattern()); } else { // Keep commands can reference the join columns with names that shadow aliases, so we block their removal keepJoinReferences.addAll(keepCommandReferences); diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/EsqlSessionCCSUtils.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/EsqlSessionCCSUtils.java index 304a54741d44b..6be243456e040 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/EsqlSessionCCSUtils.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/EsqlSessionCCSUtils.java @@ -311,7 +311,7 @@ public static void checkForCcsLicense( for (TableInfo tableInfo : indices) { Map groupedIndices; try { - groupedIndices = indicesGrouper.groupIndices(IndicesOptions.DEFAULT, tableInfo.id().index()); + groupedIndices = indicesGrouper.groupIndices(IndicesOptions.DEFAULT, tableInfo.id().indexPattern()); } catch (NoSuchRemoteClusterException e) { if (EsqlLicenseChecker.isCcsAllowed(licenseState)) { throw e; diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java index ed1ee71ff1968..ae9c12fd7c711 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java @@ -268,7 +268,7 @@ public final void test() throws Throwable { ); assumeFalse( "lookup join disabled for csv tests", - testCase.requiredCapabilities.contains(EsqlCapabilities.Cap.JOIN_LOOKUP_V11.capabilityName()) + testCase.requiredCapabilities.contains(EsqlCapabilities.Cap.JOIN_LOOKUP_V12.capabilityName()) ); assumeFalse( "can't use TERM function in csv tests", @@ -462,7 +462,7 @@ private static CsvTestsDataLoader.MultiIndexTestDataset testDatasets(LogicalPlan throw new IllegalArgumentException("unexpected index resolution to multiple entries [" + preAnalysis.indices.size() + "]"); } - String indexName = indices.get(0).id().index(); + String indexName = indices.get(0).id().indexPattern(); List datasets = new ArrayList<>(); if (indexName.endsWith("*")) { String indexPrefix = indexName.substring(0, indexName.length() - 1); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/IdentifierGenerator.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/IdentifierGenerator.java new file mode 100644 index 0000000000000..a1ae1f43ef877 --- /dev/null +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/IdentifierGenerator.java @@ -0,0 +1,116 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.esql; + +import org.elasticsearch.test.ESTestCase; + +public class IdentifierGenerator { + + /** + * Generate random identifier that could be used as a column name + */ + public static String randomIdentifier() { + return ESTestCase.randomIdentifier(); + } + + /** + * Generates one or several coma separated index patterns + */ + public static String randomIndexPatterns(Feature... features) { + return maybeQuote(String.join(",", ESTestCase.randomList(1, 5, () -> randomIndexPattern(features)))); + } + + /** + * Generates a random valid index pattern. + * You may force list of features to be included or excluded using the arguments, eg {@code randomIndexPattern(PATTERN, not(HIDDEN))}. + * Identifier could be an index or alias. It might be hidden or remote or use a pattern. + * See @link valid index patterns + */ + public static String randomIndexPattern(Feature... features) { + var validFirstCharacters = "abcdefghijklmnopqrstuvwxyz0123456789!'$^&"; + var validCharacters = validFirstCharacters + "+-_."; + + var index = new StringBuilder(); + if (canAdd(Features.HIDDEN_INDEX, features)) { + index.append('.'); + } + index.append(randomCharacterFrom(validFirstCharacters)); + for (int i = 0; i < ESTestCase.randomIntBetween(1, 100); i++) { + index.append(randomCharacterFrom(validCharacters)); + } + if (canAdd(Features.WILDCARD_PATTERN, features)) { + if (ESTestCase.randomBoolean()) { + index.append('*'); + } else { + index.insert(ESTestCase.randomIntBetween(0, index.length() - 1), '*'); + } + } else if (canAdd(Features.DATE_MATH, features)) { + // https://www.elastic.co/guide/en/elasticsearch/reference/8.17/api-conventions.html#api-date-math-index-names + index.insert(0, "<"); + index.append("-{now/"); + index.append(ESTestCase.randomFrom("d", "M", "M-1M")); + if (ESTestCase.randomBoolean()) { + index.append("{").append(ESTestCase.randomFrom("yyyy.MM", "yyyy.MM.dd")).append("}"); + } + index.append("}>"); + } + + var pattern = maybeQuote(index.toString()); + if (canAdd(Features.CROSS_CLUSTER, features)) { + var cluster = randomIdentifier(); + pattern = maybeQuote(cluster + ":" + pattern); + } + return pattern; + } + + private static char randomCharacterFrom(String str) { + return str.charAt(ESTestCase.randomInt(str.length() - 1)); + } + + public interface Feature {} + + public enum Features implements Feature { + CROSS_CLUSTER, + WILDCARD_PATTERN, + DATE_MATH, + HIDDEN_INDEX + } + + private record ExcludedFeature(Feature feature) implements Feature {} + + public static Feature without(Feature feature) { + return new ExcludedFeature(feature); + } + + private static boolean canAdd(Feature feature, Feature... features) { + for (var f : features) { + if (f.equals(feature)) { + return true; + } + if (f.equals(without(feature))) { + return false; + } + } + return ESTestCase.randomBoolean(); + } + + public static String maybeQuote(String term) { + if (term.contains("\"")) { + return term; + } + return switch (ESTestCase.randomIntBetween(0, 5)) { + case 0 -> "\"" + term + "\""; + case 1 -> "\"\"\"" + term + "\"\"\""; + default -> term;// no quotes are more likely + }; + } + + public static String unquoteIndexPattern(String term) { + return term.replace("\"\"\"", "").replace("\"", ""); + } +} diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/AnalyzerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/AnalyzerTests.java index 1c3b3a5c463e7..b01a82819e2ea 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/AnalyzerTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/AnalyzerTests.java @@ -41,7 +41,7 @@ import org.elasticsearch.xpack.esql.index.IndexResolution; import org.elasticsearch.xpack.esql.parser.ParsingException; import org.elasticsearch.xpack.esql.parser.QueryParams; -import org.elasticsearch.xpack.esql.plan.TableIdentifier; +import org.elasticsearch.xpack.esql.plan.IndexPattern; import org.elasticsearch.xpack.esql.plan.logical.Aggregate; import org.elasticsearch.xpack.esql.plan.logical.Enrich; import org.elasticsearch.xpack.esql.plan.logical.EsRelation; @@ -98,7 +98,7 @@ public class AnalyzerTests extends ESTestCase { private static final UnresolvedRelation UNRESOLVED_RELATION = new UnresolvedRelation( EMPTY, - new TableIdentifier(EMPTY, null, "idx"), + new IndexPattern(EMPTY, "idx"), false, List.of(), IndexMode.STANDARD, @@ -2143,7 +2143,7 @@ public void testLookupMatchTypeWrong() { } public void testLookupJoinUnknownIndex() { - assumeTrue("requires LOOKUP JOIN capability", EsqlCapabilities.Cap.JOIN_LOOKUP_V11.isEnabled()); + assumeTrue("requires LOOKUP JOIN capability", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()); String errorMessage = "Unknown index [foobar]"; IndexResolution missingLookupIndex = IndexResolution.invalid(errorMessage); @@ -2172,7 +2172,7 @@ public void testLookupJoinUnknownIndex() { } public void testLookupJoinUnknownField() { - assumeTrue("requires LOOKUP JOIN capability", EsqlCapabilities.Cap.JOIN_LOOKUP_V11.isEnabled()); + assumeTrue("requires LOOKUP JOIN capability", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()); String query = "FROM test | LOOKUP JOIN languages_lookup ON last_name"; String errorMessage = "1:45: Unknown column [last_name] in right side of join"; @@ -2195,7 +2195,7 @@ public void testLookupJoinUnknownField() { } public void testMultipleLookupJoinsGiveDifferentAttributes() { - assumeTrue("requires LOOKUP JOIN capability", EsqlCapabilities.Cap.JOIN_LOOKUP_V11.isEnabled()); + assumeTrue("requires LOOKUP JOIN capability", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()); // The field attributes that get contributed by different LOOKUP JOIN commands must have different name ids, // even if they have the same names. Otherwise, things like dependency analysis - like in PruneColumns - cannot work based on @@ -2225,7 +2225,7 @@ public void testMultipleLookupJoinsGiveDifferentAttributes() { } public void testLookupJoinIndexMode() { - assumeTrue("requires LOOKUP JOIN capability", EsqlCapabilities.Cap.JOIN_LOOKUP_V11.isEnabled()); + assumeTrue("requires LOOKUP JOIN capability", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()); var indexResolution = AnalyzerTestUtils.expandedDefaultIndexResolution(); var lookupResolution = AnalyzerTestUtils.defaultLookupResolution(); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/ParsingTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/ParsingTests.java index 2ee6cf6136114..859e1d788ff06 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/ParsingTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/ParsingTests.java @@ -113,7 +113,7 @@ public void testTooBigQuery() { } public void testJoinOnConstant() { - assumeTrue("LOOKUP JOIN available as snapshot only", EsqlCapabilities.Cap.JOIN_LOOKUP_V11.isEnabled()); + assumeTrue("LOOKUP JOIN available as snapshot only", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()); assertEquals( "1:55: JOIN ON clause only supports fields at the moment, found [123]", error("row languages = 1, gender = \"f\" | lookup join test on 123") @@ -129,7 +129,7 @@ public void testJoinOnConstant() { } public void testJoinOnMultipleFields() { - assumeTrue("LOOKUP JOIN available as snapshot only", EsqlCapabilities.Cap.JOIN_LOOKUP_V11.isEnabled()); + assumeTrue("LOOKUP JOIN available as snapshot only", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()); assertEquals( "1:35: JOIN ON clause only supports one field at the moment, found [2]", error("row languages = 1, gender = \"f\" | lookup join test on gender, languages") @@ -137,7 +137,7 @@ public void testJoinOnMultipleFields() { } public void testJoinTwiceOnTheSameField() { - assumeTrue("LOOKUP JOIN available as snapshot only", EsqlCapabilities.Cap.JOIN_LOOKUP_V11.isEnabled()); + assumeTrue("LOOKUP JOIN available as snapshot only", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()); assertEquals( "1:35: JOIN ON clause only supports one field at the moment, found [2]", error("row languages = 1, gender = \"f\" | lookup join test on languages, languages") @@ -145,7 +145,7 @@ public void testJoinTwiceOnTheSameField() { } public void testJoinTwiceOnTheSameField_TwoLookups() { - assumeTrue("LOOKUP JOIN available as snapshot only", EsqlCapabilities.Cap.JOIN_LOOKUP_V11.isEnabled()); + assumeTrue("LOOKUP JOIN available as snapshot only", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()); assertEquals( "1:80: JOIN ON clause only supports one field at the moment, found [2]", error("row languages = 1, gender = \"f\" | lookup join test on languages | eval x = 1 | lookup join test on gender, gender") diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/VerifierTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/VerifierTests.java index e3214411698b0..c9950bfd34f2c 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/VerifierTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/VerifierTests.java @@ -1983,7 +1983,7 @@ public void testSortByAggregate() { } public void testLookupJoinDataTypeMismatch() { - assumeTrue("requires LOOKUP JOIN capability", EsqlCapabilities.Cap.JOIN_LOOKUP_V11.isEnabled()); + assumeTrue("requires LOOKUP JOIN capability", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()); query("FROM test | EVAL language_code = languages | LOOKUP JOIN languages_lookup ON language_code"); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java index 95acc84143614..06a08c2b65936 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java @@ -4931,7 +4931,7 @@ public void testPlanSanityCheck() throws Exception { } public void testPlanSanityCheckWithBinaryPlans() throws Exception { - assumeTrue("Requires LOOKUP JOIN", EsqlCapabilities.Cap.JOIN_LOOKUP_V11.isEnabled()); + assumeTrue("Requires LOOKUP JOIN", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()); var plan = optimizedPlan(""" FROM test @@ -6006,7 +6006,7 @@ public void testLookupStats() { * \_EsRelation[languages_lookup][LOOKUP][language_code{f}#18, language_name{f}#19] */ public void testLookupJoinPushDownFilterOnJoinKeyWithRename() { - assumeTrue("Requires LOOKUP JOIN", EsqlCapabilities.Cap.JOIN_LOOKUP_V11.isEnabled()); + assumeTrue("Requires LOOKUP JOIN", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()); String query = """ FROM test @@ -6048,7 +6048,7 @@ public void testLookupJoinPushDownFilterOnJoinKeyWithRename() { * \_EsRelation[languages_lookup][LOOKUP][language_code{f}#18, language_name{f}#19] */ public void testLookupJoinPushDownFilterOnLeftSideField() { - assumeTrue("Requires LOOKUP JOIN", EsqlCapabilities.Cap.JOIN_LOOKUP_V11.isEnabled()); + assumeTrue("Requires LOOKUP JOIN", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()); String query = """ FROM test @@ -6091,7 +6091,7 @@ public void testLookupJoinPushDownFilterOnLeftSideField() { * \_EsRelation[languages_lookup][LOOKUP][language_code{f}#18, language_name{f}#19] */ public void testLookupJoinPushDownDisabledForLookupField() { - assumeTrue("Requires LOOKUP JOIN", EsqlCapabilities.Cap.JOIN_LOOKUP_V11.isEnabled()); + assumeTrue("Requires LOOKUP JOIN", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()); String query = """ FROM test @@ -6135,7 +6135,7 @@ public void testLookupJoinPushDownDisabledForLookupField() { * \_EsRelation[languages_lookup][LOOKUP][language_code{f}#19, language_name{f}#20] */ public void testLookupJoinPushDownSeparatedForConjunctionBetweenLeftAndRightField() { - assumeTrue("Requires LOOKUP JOIN", EsqlCapabilities.Cap.JOIN_LOOKUP_V11.isEnabled()); + assumeTrue("Requires LOOKUP JOIN", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()); String query = """ FROM test @@ -6186,7 +6186,7 @@ public void testLookupJoinPushDownSeparatedForConjunctionBetweenLeftAndRightFiel * \_EsRelation[languages_lookup][LOOKUP][language_code{f}#19, language_name{f}#20] */ public void testLookupJoinPushDownDisabledForDisjunctionBetweenLeftAndRightField() { - assumeTrue("Requires LOOKUP JOIN", EsqlCapabilities.Cap.JOIN_LOOKUP_V11.isEnabled()); + assumeTrue("Requires LOOKUP JOIN", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()); String query = """ FROM test @@ -6236,7 +6236,7 @@ public void testLookupJoinPushDownDisabledForDisjunctionBetweenLeftAndRightField * \_EsRelation[languages_lookup][LOOKUP][language_code{f}#18] */ public void testLookupJoinKeepNoLookupFields() { - assumeTrue("Requires LOOKUP JOIN", EsqlCapabilities.Cap.JOIN_LOOKUP_V11.isEnabled()); + assumeTrue("Requires LOOKUP JOIN", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()); String commandDiscardingFields = randomBoolean() ? "| KEEP languages" : """ | DROP _meta_field, emp_no, first_name, gender, language_code, @@ -6275,7 +6275,7 @@ public void testLookupJoinKeepNoLookupFields() { * \_EsRelation[languages_lookup][LOOKUP][language_code{f}#20, language_name{f}#21] */ public void testMultipleLookupShadowing() { - assumeTrue("Requires LOOKUP JOIN", EsqlCapabilities.Cap.JOIN_LOOKUP_V11.isEnabled()); + assumeTrue("Requires LOOKUP JOIN", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()); String query = """ FROM test diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/PhysicalPlanOptimizerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/PhysicalPlanOptimizerTests.java index 1eb7f43ee72ba..a51ad384d9488 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/PhysicalPlanOptimizerTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/PhysicalPlanOptimizerTests.java @@ -2649,7 +2649,7 @@ public void testVerifierOnMissingReferences() { } public void testVerifierOnMissingReferencesWithBinaryPlans() throws Exception { - assumeTrue("Requires LOOKUP JOIN", EsqlCapabilities.Cap.JOIN_LOOKUP_V11.isEnabled()); + assumeTrue("Requires LOOKUP JOIN", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()); // Do not assert serialization: // This will have a LookupJoinExec, which is not serializable because it doesn't leave the coordinator. @@ -7336,7 +7336,7 @@ public void testLookupThenTopN() { } public void testLookupJoinFieldLoading() throws Exception { - assumeTrue("Requires LOOKUP JOIN", EsqlCapabilities.Cap.JOIN_LOOKUP_V11.isEnabled()); + assumeTrue("Requires LOOKUP JOIN", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()); TestDataSource data = dataSetWithLookupIndices(Map.of("lookup_index", List.of("first_name", "foo", "bar", "baz"))); @@ -7413,7 +7413,7 @@ public void testLookupJoinFieldLoading() throws Exception { } public void testLookupJoinFieldLoadingTwoLookups() throws Exception { - assumeTrue("Requires LOOKUP JOIN", EsqlCapabilities.Cap.JOIN_LOOKUP_V11.isEnabled()); + assumeTrue("Requires LOOKUP JOIN", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()); TestDataSource data = dataSetWithLookupIndices( Map.of( @@ -7467,7 +7467,7 @@ public void testLookupJoinFieldLoadingTwoLookups() throws Exception { @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/119082") public void testLookupJoinFieldLoadingTwoLookupsProjectInBetween() throws Exception { - assumeTrue("Requires LOOKUP JOIN", EsqlCapabilities.Cap.JOIN_LOOKUP_V11.isEnabled()); + assumeTrue("Requires LOOKUP JOIN", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()); TestDataSource data = dataSetWithLookupIndices( Map.of( @@ -7508,7 +7508,7 @@ public void testLookupJoinFieldLoadingTwoLookupsProjectInBetween() throws Except @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/118778") public void testLookupJoinFieldLoadingDropAllFields() throws Exception { - assumeTrue("Requires LOOKUP JOIN", EsqlCapabilities.Cap.JOIN_LOOKUP_V11.isEnabled()); + assumeTrue("Requires LOOKUP JOIN", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()); TestDataSource data = dataSetWithLookupIndices(Map.of("lookup_index", List.of("first_name", "foo", "bar", "baz"))); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/parser/AbstractStatementParserTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/parser/AbstractStatementParserTests.java index 31ea4f2712b98..111c553d34a0e 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/parser/AbstractStatementParserTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/parser/AbstractStatementParserTests.java @@ -17,7 +17,7 @@ import org.elasticsearch.xpack.esql.core.expression.UnresolvedAttribute; import org.elasticsearch.xpack.esql.core.type.DataType; import org.elasticsearch.xpack.esql.expression.function.UnresolvedFunction; -import org.elasticsearch.xpack.esql.plan.TableIdentifier; +import org.elasticsearch.xpack.esql.plan.IndexPattern; import org.elasticsearch.xpack.esql.plan.logical.LogicalPlan; import org.elasticsearch.xpack.esql.plan.logical.UnresolvedRelation; @@ -72,7 +72,7 @@ static UnresolvedFunction function(String name, List args) { } static UnresolvedRelation relation(String name) { - return new UnresolvedRelation(EMPTY, new TableIdentifier(EMPTY, null, name), false, List.of(), IndexMode.STANDARD, null, "FROM"); + return new UnresolvedRelation(EMPTY, new IndexPattern(EMPTY, name), false, List.of(), IndexMode.STANDARD, null, "FROM"); } static Literal integer(int i) { diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/parser/StatementParserTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/parser/StatementParserTests.java index 3b7ae5adcd8b2..792b43433e1ee 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/parser/StatementParserTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/parser/StatementParserTests.java @@ -42,7 +42,7 @@ import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.GreaterThanOrEqual; import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.LessThan; import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.LessThanOrEqual; -import org.elasticsearch.xpack.esql.plan.TableIdentifier; +import org.elasticsearch.xpack.esql.plan.IndexPattern; import org.elasticsearch.xpack.esql.plan.logical.Aggregate; import org.elasticsearch.xpack.esql.plan.logical.Dissect; import org.elasticsearch.xpack.esql.plan.logical.Drop; @@ -62,6 +62,8 @@ import org.elasticsearch.xpack.esql.plan.logical.Rename; import org.elasticsearch.xpack.esql.plan.logical.Row; import org.elasticsearch.xpack.esql.plan.logical.UnresolvedRelation; +import org.elasticsearch.xpack.esql.plan.logical.join.JoinTypes; +import org.elasticsearch.xpack.esql.plan.logical.join.LookupJoin; import java.util.ArrayList; import java.util.HashMap; @@ -76,6 +78,11 @@ import static org.elasticsearch.xpack.esql.EsqlTestUtils.paramAsIdentifier; import static org.elasticsearch.xpack.esql.EsqlTestUtils.paramAsPattern; import static org.elasticsearch.xpack.esql.EsqlTestUtils.referenceAttribute; +import static org.elasticsearch.xpack.esql.IdentifierGenerator.Features.WILDCARD_PATTERN; +import static org.elasticsearch.xpack.esql.IdentifierGenerator.randomIndexPattern; +import static org.elasticsearch.xpack.esql.IdentifierGenerator.randomIndexPatterns; +import static org.elasticsearch.xpack.esql.IdentifierGenerator.unquoteIndexPattern; +import static org.elasticsearch.xpack.esql.IdentifierGenerator.without; import static org.elasticsearch.xpack.esql.core.expression.Literal.FALSE; import static org.elasticsearch.xpack.esql.core.expression.Literal.TRUE; import static org.elasticsearch.xpack.esql.core.tree.Source.EMPTY; @@ -2045,7 +2052,7 @@ private void assertStringAsIndexPattern(String string, String statement) { LogicalPlan from = statement(statement); assertThat(from, instanceOf(UnresolvedRelation.class)); UnresolvedRelation table = (UnresolvedRelation) from; - assertThat(table.table().index(), is(string)); + assertThat(table.indexPattern().indexPattern(), is(string)); } private void assertStringAsLookupIndexPattern(String string, String statement) { @@ -2276,20 +2283,12 @@ public void testInvalidAlias() { } private LogicalPlan unresolvedRelation(String index) { - return new UnresolvedRelation(EMPTY, new TableIdentifier(EMPTY, null, index), false, List.of(), IndexMode.STANDARD, null, "FROM"); + return new UnresolvedRelation(EMPTY, new IndexPattern(EMPTY, index), false, List.of(), IndexMode.STANDARD, null, "FROM"); } private LogicalPlan unresolvedTSRelation(String index) { List metadata = List.of(new MetadataAttribute(EMPTY, MetadataAttribute.TSID_FIELD, DataType.KEYWORD, false)); - return new UnresolvedRelation( - EMPTY, - new TableIdentifier(EMPTY, null, index), - false, - metadata, - IndexMode.TIME_SERIES, - null, - "FROM TS" - ); + return new UnresolvedRelation(EMPTY, new IndexPattern(EMPTY, index), false, metadata, IndexMode.TIME_SERIES, null, "FROM TS"); } public void testMetricWithGroupKeyAsAgg() { @@ -2939,4 +2938,30 @@ public void testNamedFunctionArgumentWithUnsupportedNamedParameterTypes() { ); } } + + public void testValidJoinPattern() { + var basePattern = randomIndexPatterns(); + var joinPattern = randomIndexPattern(without(WILDCARD_PATTERN)); + var onField = randomIdentifier(); + var type = randomFrom("", "LOOKUP "); + + var plan = statement("FROM " + basePattern + " | " + type + " JOIN " + joinPattern + " ON " + onField); + + var join = as(plan, LookupJoin.class); + assertThat(as(join.left(), UnresolvedRelation.class).indexPattern().indexPattern(), equalTo(unquoteIndexPattern(basePattern))); + assertThat(as(join.right(), UnresolvedRelation.class).indexPattern().indexPattern(), equalTo(unquoteIndexPattern(joinPattern))); + + var joinType = as(join.config().type(), JoinTypes.UsingJoinType.class); + assertThat(joinType.columns(), hasSize(1)); + assertThat(as(joinType.columns().getFirst(), UnresolvedAttribute.class).name(), equalTo(onField)); + assertThat(joinType.coreJoin().joinName(), equalTo("LEFT OUTER")); + } + + public void testInvalidJoinPatterns() { + var joinPattern = randomIndexPattern(WILDCARD_PATTERN); + expectError( + "FROM " + randomIndexPatterns() + " | JOIN " + joinPattern + " ON " + randomIdentifier(), + "invalid index pattern [" + unquoteIndexPattern(joinPattern) + "], * is not allowed in LOOKUP JOIN" + ); + } } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/session/EsqlSessionCCSUtilsTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/session/EsqlSessionCCSUtilsTests.java index 05d04ff1315e6..a84e5b144e64c 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/session/EsqlSessionCCSUtilsTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/session/EsqlSessionCCSUtilsTests.java @@ -32,7 +32,7 @@ import org.elasticsearch.xpack.esql.core.type.EsField; import org.elasticsearch.xpack.esql.index.EsIndex; import org.elasticsearch.xpack.esql.index.IndexResolution; -import org.elasticsearch.xpack.esql.plan.TableIdentifier; +import org.elasticsearch.xpack.esql.plan.IndexPattern; import org.elasticsearch.xpack.esql.type.EsFieldTests; import java.util.ArrayList; @@ -702,7 +702,7 @@ public void testCheckForCcsLicense() { // local only search does not require an enterprise license { List indices = new ArrayList<>(); - indices.add(new TableInfo(new TableIdentifier(EMPTY, null, randomFrom("idx", "idx1,idx2*")))); + indices.add(new TableInfo(new IndexPattern(EMPTY, randomFrom("idx", "idx1,idx2*")))); checkForCcsLicense(executionInfo, indices, indicesGrouper, enterpriseLicenseValid); checkForCcsLicense(executionInfo, indices, indicesGrouper, platinumLicenseValid); @@ -727,10 +727,10 @@ public void testCheckForCcsLicense() { List indices = new ArrayList<>(); final String indexExprWithRemotes = randomFrom("remote:idx", "idx1,remote:idx2*,remote:logs,c*:idx4"); if (randomBoolean()) { - indices.add(new TableInfo(new TableIdentifier(EMPTY, null, indexExprWithRemotes))); + indices.add(new TableInfo(new IndexPattern(EMPTY, indexExprWithRemotes))); } else { - indices.add(new TableInfo(new TableIdentifier(EMPTY, null, randomFrom("idx", "idx1,idx2*")))); - indices.add(new TableInfo(new TableIdentifier(EMPTY, null, indexExprWithRemotes))); + indices.add(new TableInfo(new IndexPattern(EMPTY, randomFrom("idx", "idx1,idx2*")))); + indices.add(new TableInfo(new IndexPattern(EMPTY, indexExprWithRemotes))); } // licenses that work diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/session/IndexResolverFieldNamesTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/session/IndexResolverFieldNamesTests.java index b1c9030db7a43..e7ea479d199d8 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/session/IndexResolverFieldNamesTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/session/IndexResolverFieldNamesTests.java @@ -1365,7 +1365,7 @@ public void testMetrics() { } public void testLookupJoin() { - assumeTrue("LOOKUP JOIN available as snapshot only", EsqlCapabilities.Cap.JOIN_LOOKUP_V11.isEnabled()); + assumeTrue("LOOKUP JOIN available as snapshot only", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()); assertFieldNames( "FROM employees | KEEP languages | RENAME languages AS language_code | LOOKUP JOIN languages_lookup ON language_code", Set.of("languages", "languages.*", "language_code", "language_code.*"), @@ -1374,7 +1374,7 @@ public void testLookupJoin() { } public void testLookupJoinKeep() { - assumeTrue("LOOKUP JOIN available as snapshot only", EsqlCapabilities.Cap.JOIN_LOOKUP_V11.isEnabled()); + assumeTrue("LOOKUP JOIN available as snapshot only", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()); assertFieldNames( """ FROM employees @@ -1388,7 +1388,7 @@ public void testLookupJoinKeep() { } public void testLookupJoinKeepWildcard() { - assumeTrue("LOOKUP JOIN available as snapshot only", EsqlCapabilities.Cap.JOIN_LOOKUP_V11.isEnabled()); + assumeTrue("LOOKUP JOIN available as snapshot only", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()); assertFieldNames( """ FROM employees @@ -1402,7 +1402,7 @@ public void testLookupJoinKeepWildcard() { } public void testMultiLookupJoin() { - assumeTrue("LOOKUP JOIN available as snapshot only", EsqlCapabilities.Cap.JOIN_LOOKUP_V11.isEnabled()); + assumeTrue("LOOKUP JOIN available as snapshot only", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()); assertFieldNames( """ FROM sample_data @@ -1415,7 +1415,7 @@ public void testMultiLookupJoin() { } public void testMultiLookupJoinKeepBefore() { - assumeTrue("LOOKUP JOIN available as snapshot only", EsqlCapabilities.Cap.JOIN_LOOKUP_V11.isEnabled()); + assumeTrue("LOOKUP JOIN available as snapshot only", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()); assertFieldNames( """ FROM sample_data @@ -1429,7 +1429,7 @@ public void testMultiLookupJoinKeepBefore() { } public void testMultiLookupJoinKeepBetween() { - assumeTrue("LOOKUP JOIN available as snapshot only", EsqlCapabilities.Cap.JOIN_LOOKUP_V11.isEnabled()); + assumeTrue("LOOKUP JOIN available as snapshot only", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()); assertFieldNames( """ FROM sample_data @@ -1454,7 +1454,7 @@ public void testMultiLookupJoinKeepBetween() { } public void testMultiLookupJoinKeepAfter() { - assumeTrue("LOOKUP JOIN available as snapshot only", EsqlCapabilities.Cap.JOIN_LOOKUP_V11.isEnabled()); + assumeTrue("LOOKUP JOIN available as snapshot only", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()); assertFieldNames( """ FROM sample_data @@ -1481,7 +1481,7 @@ public void testMultiLookupJoinKeepAfter() { } public void testMultiLookupJoinKeepAfterWildcard() { - assumeTrue("LOOKUP JOIN available as snapshot only", EsqlCapabilities.Cap.JOIN_LOOKUP_V11.isEnabled()); + assumeTrue("LOOKUP JOIN available as snapshot only", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()); assertFieldNames( """ FROM sample_data @@ -1495,7 +1495,7 @@ public void testMultiLookupJoinKeepAfterWildcard() { } public void testMultiLookupJoinSameIndex() { - assumeTrue("LOOKUP JOIN available as snapshot only", EsqlCapabilities.Cap.JOIN_LOOKUP_V11.isEnabled()); + assumeTrue("LOOKUP JOIN available as snapshot only", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()); assertFieldNames( """ FROM sample_data @@ -1509,7 +1509,7 @@ public void testMultiLookupJoinSameIndex() { } public void testMultiLookupJoinSameIndexKeepBefore() { - assumeTrue("LOOKUP JOIN available as snapshot only", EsqlCapabilities.Cap.JOIN_LOOKUP_V11.isEnabled()); + assumeTrue("LOOKUP JOIN available as snapshot only", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()); assertFieldNames( """ FROM sample_data @@ -1524,7 +1524,7 @@ public void testMultiLookupJoinSameIndexKeepBefore() { } public void testMultiLookupJoinSameIndexKeepBetween() { - assumeTrue("LOOKUP JOIN available as snapshot only", EsqlCapabilities.Cap.JOIN_LOOKUP_V11.isEnabled()); + assumeTrue("LOOKUP JOIN available as snapshot only", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()); assertFieldNames( """ FROM sample_data @@ -1550,7 +1550,7 @@ public void testMultiLookupJoinSameIndexKeepBetween() { } public void testMultiLookupJoinSameIndexKeepAfter() { - assumeTrue("LOOKUP JOIN available as snapshot only", EsqlCapabilities.Cap.JOIN_LOOKUP_V11.isEnabled()); + assumeTrue("LOOKUP JOIN available as snapshot only", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()); assertFieldNames( """ FROM sample_data diff --git a/x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/StandardVersusLogsIndexModeChallengeRestIT.java b/x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/StandardVersusLogsIndexModeChallengeRestIT.java index 1e4c28e72aaeb..f18e57c229345 100644 --- a/x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/StandardVersusLogsIndexModeChallengeRestIT.java +++ b/x-pack/plugin/logsdb/src/javaRestTest/java/org/elasticsearch/xpack/logsdb/qa/StandardVersusLogsIndexModeChallengeRestIT.java @@ -17,6 +17,8 @@ import org.elasticsearch.common.time.FormatNames; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.logsdb.datageneration.matchers.MatchResult; +import org.elasticsearch.logsdb.datageneration.matchers.Matcher; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.search.aggregations.AggregationBuilders; import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval; @@ -27,8 +29,6 @@ import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xcontent.XContentFactory; import org.elasticsearch.xcontent.XContentType; -import org.elasticsearch.xpack.logsdb.qa.matchers.MatchResult; -import org.elasticsearch.xpack.logsdb.qa.matchers.Matcher; import org.hamcrest.Matchers; import java.io.IOException; diff --git a/x-pack/plugin/migrate/src/internalClusterTest/java/org/elasticsearch/xpack/migrate/action/CreateIndexFromSourceActionIT.java b/x-pack/plugin/migrate/src/internalClusterTest/java/org/elasticsearch/xpack/migrate/action/CreateIndexFromSourceActionIT.java index 3b523857eb6e1..5220e17618a34 100644 --- a/x-pack/plugin/migrate/src/internalClusterTest/java/org/elasticsearch/xpack/migrate/action/CreateIndexFromSourceActionIT.java +++ b/x-pack/plugin/migrate/src/internalClusterTest/java/org/elasticsearch/xpack/migrate/action/CreateIndexFromSourceActionIT.java @@ -32,7 +32,6 @@ import java.util.Map; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.xpack.migrate.action.ReindexDataStreamAction.REINDEX_DATA_STREAM_FEATURE_FLAG; public class CreateIndexFromSourceActionIT extends ESIntegTestCase { @@ -42,8 +41,6 @@ protected Collection> nodePlugins() { } public void testOldSettingsManuallyFiltered() throws Exception { - assumeTrue("requires the migration reindex feature flag", REINDEX_DATA_STREAM_FEATURE_FLAG.isEnabled()); - var numShards = randomIntBetween(1, 10); var staticSettings = Settings.builder() // setting to filter @@ -77,8 +74,6 @@ public void testOldSettingsManuallyFiltered() throws Exception { } public void testDestIndexCreated() throws Exception { - assumeTrue("requires the migration reindex feature flag", REINDEX_DATA_STREAM_FEATURE_FLAG.isEnabled()); - var sourceIndex = randomAlphaOfLength(20).toLowerCase(Locale.ROOT); indicesAdmin().create(new CreateIndexRequest(sourceIndex)).get(); @@ -96,8 +91,6 @@ public void testDestIndexCreated() throws Exception { } public void testSettingsCopiedFromSource() throws Exception { - assumeTrue("requires the migration reindex feature flag", REINDEX_DATA_STREAM_FEATURE_FLAG.isEnabled()); - // start with a static setting var numShards = randomIntBetween(1, 10); var staticSettings = Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, numShards).build(); @@ -122,8 +115,6 @@ public void testSettingsCopiedFromSource() throws Exception { } public void testMappingsCopiedFromSource() { - assumeTrue("requires the migration reindex feature flag", REINDEX_DATA_STREAM_FEATURE_FLAG.isEnabled()); - var sourceIndex = randomAlphaOfLength(20).toLowerCase(Locale.ROOT); String mapping = """ { @@ -157,8 +148,6 @@ public void testMappingsCopiedFromSource() { } public void testSettingsOverridden() throws Exception { - assumeTrue("requires the migration reindex feature flag", REINDEX_DATA_STREAM_FEATURE_FLAG.isEnabled()); - var numShardsSource = randomIntBetween(1, 10); var numReplicasSource = randomIntBetween(0, 10); var sourceIndex = randomAlphaOfLength(20).toLowerCase(Locale.ROOT); @@ -191,8 +180,6 @@ public void testSettingsOverridden() throws Exception { } public void testSettingsNullOverride() throws Exception { - assumeTrue("requires the migration reindex feature flag", REINDEX_DATA_STREAM_FEATURE_FLAG.isEnabled()); - var sourceIndex = randomAlphaOfLength(20).toLowerCase(Locale.ROOT); var sourceSettings = Settings.builder() .put(IndexMetadata.SETTING_BLOCKS_WRITE, true) @@ -223,8 +210,6 @@ public void testSettingsNullOverride() throws Exception { } public void testRemoveIndexBlocksByDefault() throws Exception { - assumeTrue("requires the migration reindex feature flag", REINDEX_DATA_STREAM_FEATURE_FLAG.isEnabled()); - var sourceIndex = randomAlphaOfLength(20).toLowerCase(Locale.ROOT); var sourceSettings = Settings.builder() @@ -257,8 +242,6 @@ public void testRemoveIndexBlocksByDefault() throws Exception { } public void testMappingsOverridden() { - assumeTrue("requires the migration reindex feature flag", REINDEX_DATA_STREAM_FEATURE_FLAG.isEnabled()); - var sourceIndex = randomAlphaOfLength(20).toLowerCase(Locale.ROOT); String sourceMapping = """ { diff --git a/x-pack/plugin/migrate/src/internalClusterTest/java/org/elasticsearch/xpack/migrate/action/ReindexDataStreamTransportActionIT.java b/x-pack/plugin/migrate/src/internalClusterTest/java/org/elasticsearch/xpack/migrate/action/ReindexDataStreamTransportActionIT.java index 04caf3dbaa9d1..b6ff76095ac16 100644 --- a/x-pack/plugin/migrate/src/internalClusterTest/java/org/elasticsearch/xpack/migrate/action/ReindexDataStreamTransportActionIT.java +++ b/x-pack/plugin/migrate/src/internalClusterTest/java/org/elasticsearch/xpack/migrate/action/ReindexDataStreamTransportActionIT.java @@ -40,7 +40,6 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReference; -import static org.elasticsearch.xpack.migrate.action.ReindexDataStreamAction.REINDEX_DATA_STREAM_FEATURE_FLAG; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; @@ -52,7 +51,6 @@ protected Collection> nodePlugins() { } public void testNonExistentDataStream() { - assumeTrue("requires the migration reindex feature flag", REINDEX_DATA_STREAM_FEATURE_FLAG.isEnabled()); String nonExistentDataStreamName = randomAlphaOfLength(50); ReindexDataStreamRequest reindexDataStreamRequest = new ReindexDataStreamRequest( ReindexDataStreamAction.Mode.UPGRADE, @@ -65,7 +63,6 @@ public void testNonExistentDataStream() { } public void testAlreadyUpToDateDataStream() throws Exception { - assumeTrue("requires the migration reindex feature flag", REINDEX_DATA_STREAM_FEATURE_FLAG.isEnabled()); String dataStreamName = randomAlphaOfLength(50).toLowerCase(Locale.ROOT); ReindexDataStreamRequest reindexDataStreamRequest = new ReindexDataStreamRequest( ReindexDataStreamAction.Mode.UPGRADE, diff --git a/x-pack/plugin/migrate/src/internalClusterTest/java/org/elasticsearch/xpack/migrate/action/ReindexDatastreamIndexTransportActionIT.java b/x-pack/plugin/migrate/src/internalClusterTest/java/org/elasticsearch/xpack/migrate/action/ReindexDatastreamIndexTransportActionIT.java index cfd4f0901336d..0ad7dc45d4df8 100644 --- a/x-pack/plugin/migrate/src/internalClusterTest/java/org/elasticsearch/xpack/migrate/action/ReindexDatastreamIndexTransportActionIT.java +++ b/x-pack/plugin/migrate/src/internalClusterTest/java/org/elasticsearch/xpack/migrate/action/ReindexDatastreamIndexTransportActionIT.java @@ -53,7 +53,6 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; -import static org.elasticsearch.xpack.migrate.action.ReindexDataStreamAction.REINDEX_DATA_STREAM_FEATURE_FLAG; import static org.hamcrest.Matchers.equalTo; public class ReindexDatastreamIndexTransportActionIT extends ESIntegTestCase { @@ -77,8 +76,6 @@ protected Collection> nodePlugins() { } public void testDestIndexDeletedIfExists() throws Exception { - assumeTrue("requires the migration reindex feature flag", REINDEX_DATA_STREAM_FEATURE_FLAG.isEnabled()); - // empty source index var sourceIndex = randomAlphaOfLength(20).toLowerCase(Locale.ROOT); indicesAdmin().create(new CreateIndexRequest(sourceIndex)).get(); @@ -87,6 +84,7 @@ public void testDestIndexDeletedIfExists() throws Exception { var destIndex = ReindexDataStreamIndexTransportAction.generateDestIndexName(sourceIndex); indicesAdmin().create(new CreateIndexRequest(destIndex)).actionGet(); indexDocs(destIndex, 10); + indicesAdmin().refresh(new RefreshRequest(destIndex)).actionGet(); assertHitCount(prepareSearch(destIndex).setSize(0), 10); // call reindex @@ -98,8 +96,6 @@ public void testDestIndexDeletedIfExists() throws Exception { } public void testDestIndexNameSet_noDotPrefix() throws Exception { - assumeTrue("requires the migration reindex feature flag", REINDEX_DATA_STREAM_FEATURE_FLAG.isEnabled()); - var sourceIndex = randomAlphaOfLength(20).toLowerCase(Locale.ROOT); indicesAdmin().create(new CreateIndexRequest(sourceIndex)).get(); @@ -112,7 +108,6 @@ public void testDestIndexNameSet_noDotPrefix() throws Exception { } public void testDestIndexNameSet_withDotPrefix() throws Exception { - assumeTrue("requires the migration reindex feature flag", REINDEX_DATA_STREAM_FEATURE_FLAG.isEnabled()); var sourceIndex = "." + randomAlphaOfLength(20).toLowerCase(Locale.ROOT); indicesAdmin().create(new CreateIndexRequest(sourceIndex)).get(); @@ -126,8 +121,6 @@ public void testDestIndexNameSet_withDotPrefix() throws Exception { } public void testDestIndexContainsDocs() throws Exception { - assumeTrue("requires the migration reindex feature flag", REINDEX_DATA_STREAM_FEATURE_FLAG.isEnabled()); - // source index with docs var numDocs = randomIntBetween(1, 100); var sourceIndex = randomAlphaOfLength(20).toLowerCase(Locale.ROOT); @@ -144,8 +137,6 @@ public void testDestIndexContainsDocs() throws Exception { } public void testSetSourceToBlockWrites() throws Exception { - assumeTrue("requires the migration reindex feature flag", REINDEX_DATA_STREAM_FEATURE_FLAG.isEnabled()); - var settings = randomBoolean() ? Settings.builder().put(IndexMetadata.SETTING_BLOCKS_WRITE, true).build() : Settings.EMPTY; // empty source index @@ -162,8 +153,6 @@ public void testSetSourceToBlockWrites() throws Exception { } public void testSettingsAddedBeforeReindex() throws Exception { - assumeTrue("requires the migration reindex feature flag", REINDEX_DATA_STREAM_FEATURE_FLAG.isEnabled()); - // start with a static setting var numShards = randomIntBetween(1, 10); var staticSettings = Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, numShards).build(); @@ -192,22 +181,8 @@ public void testSettingsAddedBeforeReindex() throws Exception { } public void testMappingsAddedToDestIndex() throws Exception { - assumeTrue("requires the migration reindex feature flag", REINDEX_DATA_STREAM_FEATURE_FLAG.isEnabled()); - var sourceIndex = randomAlphaOfLength(20).toLowerCase(Locale.ROOT); - String mapping = """ - { - "_doc":{ - "dynamic":"strict", - "properties":{ - "foo1":{ - "type":"text" - } - } - } - } - """; - indicesAdmin().create(new CreateIndexRequest(sourceIndex).mapping(mapping)).actionGet(); + indicesAdmin().create(new CreateIndexRequest(sourceIndex).mapping(MAPPING)).actionGet(); // call reindex var destIndex = client().execute(ReindexDataStreamIndexAction.INSTANCE, new ReindexDataStreamIndexAction.Request(sourceIndex)) @@ -226,8 +201,6 @@ public void testMappingsAddedToDestIndex() throws Exception { } public void testFailIfMetadataBlockSet() { - assumeTrue("requires the migration reindex feature flag", REINDEX_DATA_STREAM_FEATURE_FLAG.isEnabled()); - var sourceIndex = randomAlphaOfLength(20).toLowerCase(Locale.ROOT); var settings = Settings.builder().put(IndexMetadata.SETTING_BLOCKS_METADATA, true).build(); indicesAdmin().create(new CreateIndexRequest(sourceIndex, settings)).actionGet(); @@ -242,8 +215,6 @@ public void testFailIfMetadataBlockSet() { } public void testFailIfReadBlockSet() { - assumeTrue("requires the migration reindex feature flag", REINDEX_DATA_STREAM_FEATURE_FLAG.isEnabled()); - var sourceIndex = randomAlphaOfLength(20).toLowerCase(Locale.ROOT); var settings = Settings.builder().put(IndexMetadata.SETTING_BLOCKS_READ, true).build(); indicesAdmin().create(new CreateIndexRequest(sourceIndex, settings)).actionGet(); @@ -258,8 +229,6 @@ public void testFailIfReadBlockSet() { } public void testReadOnlyBlocksNotAddedBack() { - assumeTrue("requires the migration reindex feature flag", REINDEX_DATA_STREAM_FEATURE_FLAG.isEnabled()); - var sourceIndex = randomAlphaOfLength(20).toLowerCase(Locale.ROOT); var settings = Settings.builder() .put(IndexMetadata.SETTING_READ_ONLY, randomBoolean()) @@ -283,8 +252,6 @@ public void testReadOnlyBlocksNotAddedBack() { } public void testUpdateSettingsDefaultsRestored() { - assumeTrue("requires the migration reindex feature flag", REINDEX_DATA_STREAM_FEATURE_FLAG.isEnabled()); - // ESIntegTestCase creates a template random_index_template which contains a value for number_of_replicas. // Since this test checks the behavior of default settings, there cannot be a value for number_of_replicas, // so we delete the template within this method. This has no effect on other tests which will still @@ -315,8 +282,6 @@ public void testUpdateSettingsDefaultsRestored() { } public void testSettingsAndMappingsFromTemplate() throws IOException { - assumeTrue("requires the migration reindex feature flag", REINDEX_DATA_STREAM_FEATURE_FLAG.isEnabled()); - var numShards = randomIntBetween(1, 10); var numReplicas = randomIntBetween(0, 10); @@ -337,6 +302,12 @@ public void testSettingsAndMappingsFromTemplate() throws IOException { var sourceIndex = "logs-" + randomAlphaOfLength(20).toLowerCase(Locale.ROOT); indicesAdmin().create(new CreateIndexRequest(sourceIndex)).actionGet(); + { + var indexRequest = new IndexRequest(sourceIndex); + indexRequest.source("{ \"foo1\": \"cheese\" }", XContentType.JSON); + client().index(indexRequest).actionGet(); + } + // call reindex var destIndex = client().execute(ReindexDataStreamIndexAction.INSTANCE, new ReindexDataStreamIndexAction.Request(sourceIndex)) .actionGet() @@ -359,6 +330,9 @@ public void testSettingsAndMappingsFromTemplate() throws IOException { // sanity check specific value from dest mapping assertEquals("text", XContentMapValues.extractValue("properties.foo1.type", destMappings)); } + + // verify doc was successfully added + assertHitCount(prepareSearch(destIndex).setSize(0), 1); } private static final String TSDB_MAPPING = """ @@ -395,8 +369,6 @@ public void testSettingsAndMappingsFromTemplate() throws IOException { """; public void testTsdbStartEndSet() throws Exception { - assumeTrue("requires the migration reindex feature flag", REINDEX_DATA_STREAM_FEATURE_FLAG.isEnabled()); - var templateSettings = Settings.builder().put("index.mode", "time_series"); if (randomBoolean()) { templateSettings.put("index.routing_path", "metricset"); @@ -455,12 +427,10 @@ public void testTsdbStartEndSet() throws Exception { assertEquals(startTime, destStart); assertEquals(endTime, destEnd); - } - // TODO more logsdb/tsdb specific tests - // TODO more data stream specific tests (how are data streams indices are different from regular indices?) - // TODO check other IndexMetadata fields that need to be fixed after the fact - // TODO what happens if don't have necessary perms for a given index? + // verify doc was successfully added + assertHitCount(prepareSearch(destIndex).setSize(0), 1); + } private static void cleanupMetadataBlocks(String index) { var settings = Settings.builder() @@ -483,7 +453,6 @@ private static void indexDocs(String index, int numDocs) { } BulkResponse bulkResponse = client().bulk(bulkRequest).actionGet(); assertThat(bulkResponse.getItems().length, equalTo(numDocs)); - indicesAdmin().refresh(new RefreshRequest(index)).actionGet(); } private static String formatInstant(Instant instant) { diff --git a/x-pack/plugin/migrate/src/main/java/org/elasticsearch/xpack/migrate/MigratePlugin.java b/x-pack/plugin/migrate/src/main/java/org/elasticsearch/xpack/migrate/MigratePlugin.java index 10cf498c85bf0..f5f8beba26d8f 100644 --- a/x-pack/plugin/migrate/src/main/java/org/elasticsearch/xpack/migrate/MigratePlugin.java +++ b/x-pack/plugin/migrate/src/main/java/org/elasticsearch/xpack/migrate/MigratePlugin.java @@ -60,7 +60,6 @@ import java.util.function.Supplier; import static org.elasticsearch.xpack.core.ClientHelper.REINDEX_DATA_STREAM_ORIGIN; -import static org.elasticsearch.xpack.migrate.action.ReindexDataStreamAction.REINDEX_DATA_STREAM_FEATURE_FLAG; import static org.elasticsearch.xpack.migrate.action.ReindexDataStreamIndexTransportAction.REINDEX_MAX_REQUESTS_PER_SECOND_SETTING; import static org.elasticsearch.xpack.migrate.task.ReindexDataStreamPersistentTaskExecutor.MAX_CONCURRENT_INDICES_REINDEXED_PER_DATA_STREAM_SETTING; @@ -79,67 +78,55 @@ public List getRestHandlers( Predicate clusterSupportsFeature ) { List handlers = new ArrayList<>(); - if (REINDEX_DATA_STREAM_FEATURE_FLAG.isEnabled()) { - handlers.add(new RestMigrationReindexAction()); - handlers.add(new RestGetMigrationReindexStatusAction()); - handlers.add(new RestCancelReindexDataStreamAction()); - handlers.add(new RestCreateIndexFromSourceAction()); - } + handlers.add(new RestMigrationReindexAction()); + handlers.add(new RestGetMigrationReindexStatusAction()); + handlers.add(new RestCancelReindexDataStreamAction()); + handlers.add(new RestCreateIndexFromSourceAction()); return handlers; } @Override public List> getActions() { List> actions = new ArrayList<>(); - if (REINDEX_DATA_STREAM_FEATURE_FLAG.isEnabled()) { - actions.add(new ActionHandler<>(ReindexDataStreamAction.INSTANCE, ReindexDataStreamTransportAction.class)); - actions.add(new ActionHandler<>(GetMigrationReindexStatusAction.INSTANCE, GetMigrationReindexStatusTransportAction.class)); - actions.add(new ActionHandler<>(CancelReindexDataStreamAction.INSTANCE, CancelReindexDataStreamTransportAction.class)); - actions.add(new ActionHandler<>(ReindexDataStreamIndexAction.INSTANCE, ReindexDataStreamIndexTransportAction.class)); - actions.add(new ActionHandler<>(CreateIndexFromSourceAction.INSTANCE, CreateIndexFromSourceTransportAction.class)); - } + actions.add(new ActionHandler<>(ReindexDataStreamAction.INSTANCE, ReindexDataStreamTransportAction.class)); + actions.add(new ActionHandler<>(GetMigrationReindexStatusAction.INSTANCE, GetMigrationReindexStatusTransportAction.class)); + actions.add(new ActionHandler<>(CancelReindexDataStreamAction.INSTANCE, CancelReindexDataStreamTransportAction.class)); + actions.add(new ActionHandler<>(ReindexDataStreamIndexAction.INSTANCE, ReindexDataStreamIndexTransportAction.class)); + actions.add(new ActionHandler<>(CreateIndexFromSourceAction.INSTANCE, CreateIndexFromSourceTransportAction.class)); return actions; } @Override public List getNamedXContent() { - if (REINDEX_DATA_STREAM_FEATURE_FLAG.isEnabled()) { - return List.of( - new NamedXContentRegistry.Entry( - PersistentTaskState.class, - new ParseField(ReindexDataStreamPersistentTaskState.NAME), - ReindexDataStreamPersistentTaskState::fromXContent - ), - new NamedXContentRegistry.Entry( - PersistentTaskParams.class, - new ParseField(ReindexDataStreamTaskParams.NAME), - ReindexDataStreamTaskParams::fromXContent - ) - ); - } else { - return List.of(); - } + return List.of( + new NamedXContentRegistry.Entry( + PersistentTaskState.class, + new ParseField(ReindexDataStreamPersistentTaskState.NAME), + ReindexDataStreamPersistentTaskState::fromXContent + ), + new NamedXContentRegistry.Entry( + PersistentTaskParams.class, + new ParseField(ReindexDataStreamTaskParams.NAME), + ReindexDataStreamTaskParams::fromXContent + ) + ); } @Override public List getNamedWriteables() { - if (REINDEX_DATA_STREAM_FEATURE_FLAG.isEnabled()) { - return List.of( - new NamedWriteableRegistry.Entry( - PersistentTaskState.class, - ReindexDataStreamPersistentTaskState.NAME, - ReindexDataStreamPersistentTaskState::new - ), - new NamedWriteableRegistry.Entry( - PersistentTaskParams.class, - ReindexDataStreamTaskParams.NAME, - ReindexDataStreamTaskParams::new - ), - new NamedWriteableRegistry.Entry(Task.Status.class, ReindexDataStreamStatus.NAME, ReindexDataStreamStatus::new) - ); - } else { - return List.of(); - } + return List.of( + new NamedWriteableRegistry.Entry( + PersistentTaskState.class, + ReindexDataStreamPersistentTaskState.NAME, + ReindexDataStreamPersistentTaskState::new + ), + new NamedWriteableRegistry.Entry( + PersistentTaskParams.class, + ReindexDataStreamTaskParams.NAME, + ReindexDataStreamTaskParams::new + ), + new NamedWriteableRegistry.Entry(Task.Status.class, ReindexDataStreamStatus.NAME, ReindexDataStreamStatus::new) + ); } @Override @@ -150,18 +137,14 @@ public List> getPersistentTasksExecutor( SettingsModule settingsModule, IndexNameExpressionResolver expressionResolver ) { - if (REINDEX_DATA_STREAM_FEATURE_FLAG.isEnabled()) { - return List.of( - new ReindexDataStreamPersistentTaskExecutor( - new OriginSettingClient(client, REINDEX_DATA_STREAM_ORIGIN), - clusterService, - ReindexDataStreamTask.TASK_NAME, - threadPool - ) - ); - } else { - return List.of(); - } + return List.of( + new ReindexDataStreamPersistentTaskExecutor( + new OriginSettingClient(client, REINDEX_DATA_STREAM_ORIGIN), + clusterService, + ReindexDataStreamTask.TASK_NAME, + threadPool + ) + ); } @Override diff --git a/x-pack/plugin/migrate/src/main/java/org/elasticsearch/xpack/migrate/action/ReindexDataStreamAction.java b/x-pack/plugin/migrate/src/main/java/org/elasticsearch/xpack/migrate/action/ReindexDataStreamAction.java index 17925eb04851b..faf8982b79bf0 100644 --- a/x-pack/plugin/migrate/src/main/java/org/elasticsearch/xpack/migrate/action/ReindexDataStreamAction.java +++ b/x-pack/plugin/migrate/src/main/java/org/elasticsearch/xpack/migrate/action/ReindexDataStreamAction.java @@ -15,7 +15,6 @@ import org.elasticsearch.action.support.master.AcknowledgedResponse; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; -import org.elasticsearch.common.util.FeatureFlag; import org.elasticsearch.features.NodeFeature; import org.elasticsearch.xcontent.ConstructingObjectParser; import org.elasticsearch.xcontent.ParseField; @@ -29,7 +28,6 @@ import java.util.function.Predicate; public class ReindexDataStreamAction extends ActionType { - public static final FeatureFlag REINDEX_DATA_STREAM_FEATURE_FLAG = new FeatureFlag("reindex_data_stream"); public static final String TASK_ID_PREFIX = "reindex-data-stream-"; public static final ReindexDataStreamAction INSTANCE = new ReindexDataStreamAction(); diff --git a/x-pack/plugin/migrate/src/main/java/org/elasticsearch/xpack/migrate/action/ReindexDataStreamIndexTransportAction.java b/x-pack/plugin/migrate/src/main/java/org/elasticsearch/xpack/migrate/action/ReindexDataStreamIndexTransportAction.java index b915eb3cd3e28..d3fe27006e82e 100644 --- a/x-pack/plugin/migrate/src/main/java/org/elasticsearch/xpack/migrate/action/ReindexDataStreamIndexTransportAction.java +++ b/x-pack/plugin/migrate/src/main/java/org/elasticsearch/xpack/migrate/action/ReindexDataStreamIndexTransportAction.java @@ -23,6 +23,7 @@ import org.elasticsearch.action.support.HandledTransportAction; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.action.support.SubscribableListener; +import org.elasticsearch.action.support.broadcast.BroadcastResponse; import org.elasticsearch.action.support.master.AcknowledgedResponse; import org.elasticsearch.client.internal.Client; import org.elasticsearch.cluster.block.ClusterBlockException; @@ -140,6 +141,7 @@ protected void doExecute( } SubscribableListener.newForked(l -> setBlockWrites(sourceIndexName, l, taskId)) + .andThen(l -> refresh(sourceIndexName, l, taskId)) .andThen(l -> deleteDestIfExists(destIndexName, l, taskId)) .andThen(l -> createIndex(sourceIndex, destIndexName, l, taskId)) .andThen(l -> reindex(sourceIndexName, destIndexName, l, taskId)) @@ -175,6 +177,13 @@ public void onFailure(Exception e) { }, parentTaskId); } + private void refresh(String sourceIndexName, ActionListener listener, TaskId parentTaskId) { + logger.debug("Refreshing source index [{}]", sourceIndexName); + var refreshRequest = new RefreshRequest(sourceIndexName); + refreshRequest.setParentTask(parentTaskId); + client.execute(RefreshAction.INSTANCE, refreshRequest, listener); + } + private void deleteDestIfExists(String destIndexName, ActionListener listener, TaskId parentTaskId) { logger.debug("Attempting to delete index [{}]", destIndexName); var deleteIndexRequest = new DeleteIndexRequest(destIndexName).indicesOptions(IGNORE_MISSING_OPTIONS) diff --git a/x-pack/plugin/migrate/src/main/java/org/elasticsearch/xpack/migrate/rest/RestMigrationReindexAction.java b/x-pack/plugin/migrate/src/main/java/org/elasticsearch/xpack/migrate/rest/RestMigrationReindexAction.java index a89f056477d2c..1578228ea1b63 100644 --- a/x-pack/plugin/migrate/src/main/java/org/elasticsearch/xpack/migrate/rest/RestMigrationReindexAction.java +++ b/x-pack/plugin/migrate/src/main/java/org/elasticsearch/xpack/migrate/rest/RestMigrationReindexAction.java @@ -20,13 +20,10 @@ import org.elasticsearch.xpack.migrate.action.ReindexDataStreamAction; import java.io.IOException; -import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.Set; import static org.elasticsearch.rest.RestRequest.Method.POST; -import static org.elasticsearch.xpack.migrate.action.ReindexDataStreamAction.REINDEX_DATA_STREAM_FEATURE_FLAG; public class RestMigrationReindexAction extends BaseRestHandler { public static final String MIGRATION_REINDEX_CAPABILITY = "migration_reindex"; @@ -56,11 +53,7 @@ protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient cli @Override public Set supportedCapabilities() { - Set capabilities = new HashSet<>(); - if (REINDEX_DATA_STREAM_FEATURE_FLAG.isEnabled()) { - capabilities.add(MIGRATION_REINDEX_CAPABILITY); - } - return Collections.unmodifiableSet(capabilities); + return Set.of(MIGRATION_REINDEX_CAPABILITY); } static class ReindexDataStreamRestToXContentListener extends RestBuilderListener { diff --git a/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/LearningToRankExplainIT.java b/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/LearningToRankExplainIT.java new file mode 100644 index 0000000000000..d05f4a37d5501 --- /dev/null +++ b/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/LearningToRankExplainIT.java @@ -0,0 +1,223 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.ml.integration; + +import org.elasticsearch.action.bulk.BulkRequestBuilder; +import org.elasticsearch.action.bulk.BulkResponse; +import org.elasticsearch.action.index.IndexRequest; +import org.elasticsearch.action.support.WriteRequest; +import org.elasticsearch.cluster.metadata.IndexMetadata; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.core.Predicates; +import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.search.SearchHit; +import org.elasticsearch.search.builder.SearchSourceBuilder; +import org.elasticsearch.xcontent.XContentParser; +import org.elasticsearch.xcontent.json.JsonXContent; +import org.elasticsearch.xpack.core.ml.action.PutTrainedModelAction; +import org.elasticsearch.xpack.core.ml.inference.TrainedModelConfig; +import org.elasticsearch.xpack.core.ml.inference.TrainedModelDefinition; +import org.elasticsearch.xpack.core.ml.inference.trainedmodel.LearningToRankConfig; +import org.elasticsearch.xpack.core.ml.inference.trainedmodel.TargetType; +import org.elasticsearch.xpack.core.ml.inference.trainedmodel.ensemble.Ensemble; +import org.elasticsearch.xpack.core.ml.inference.trainedmodel.ltr.QueryExtractorBuilder; +import org.elasticsearch.xpack.core.ml.inference.trainedmodel.tree.Tree; +import org.elasticsearch.xpack.core.ml.inference.trainedmodel.tree.TreeNode; +import org.elasticsearch.xpack.core.ml.job.config.Operator; +import org.elasticsearch.xpack.core.ml.utils.QueryProvider; +import org.elasticsearch.xpack.ml.support.BaseMlIntegTestCase; +import org.junit.Before; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertResponse; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; + +public class LearningToRankExplainIT extends BaseMlIntegTestCase { + + private static final String LTR_SEARCH_INDEX = "ltr-search-index"; + private static final String LTR_MODEL = "ltr-model"; + private static final int NUMBER_OF_NODES = 3; + private static final String DEFAULT_SEARCH_REQUEST_BODY = """ + { + "query": { + "match": { "product": { "query": "TV" } } + }, + "rescore": { + "window_size": 10, + "learning_to_rank": { + "model_id": "ltr-model", + "params": { "keyword": "TV" } + } + } + }"""; + + @Before + public void setupCluster() throws IOException { + internalCluster().ensureAtLeastNumDataNodes(NUMBER_OF_NODES); + ensureStableCluster(); + createLtrModel(); + } + + public void testLtrExplainWithSingleShard() throws IOException { + runLtrExplainTest(1, 1, 2, new float[] { 15f, 11f }); + } + + public void testLtrExplainWithMultipleShards() throws IOException { + runLtrExplainTest(randomIntBetween(2, NUMBER_OF_NODES), 0, 2, new float[] { 15f, 11f }); + } + + public void testLtrExplainWithReplicas() throws IOException { + runLtrExplainTest(1, randomIntBetween(1, NUMBER_OF_NODES - 1), 2, new float[] { 15f, 11f }); + } + + public void testLtrExplainWithMultipleShardsAndReplicas() throws IOException { + runLtrExplainTest(randomIntBetween(2, NUMBER_OF_NODES), randomIntBetween(1, NUMBER_OF_NODES - 1), 2, new float[] { 15f, 11f }); + } + + private void runLtrExplainTest(int numberOfShards, int numberOfReplicas, long expectedTotalHits, float[] expectedScores) + throws IOException { + createLtrIndex(numberOfShards, numberOfReplicas); + try (XContentParser parser = createParser(JsonXContent.jsonXContent, DEFAULT_SEARCH_REQUEST_BODY)) { + assertResponse( + client().prepareSearch(LTR_SEARCH_INDEX) + .setSource(new SearchSourceBuilder().parseXContent(parser, true, Predicates.always())) + .setExplain(true), + searchResponse -> { + assertThat(searchResponse.getHits().getTotalHits().value(), equalTo(expectedTotalHits)); + for (int i = 0; i < expectedScores.length; i++) { + // Check expected score + SearchHit hit = searchResponse.getHits().getHits()[i]; + assertThat(hit.getScore(), equalTo(expectedScores[i])); + + // Check explanation is present and contains the right data + assertThat(hit.getExplanation(), notNullValue()); + assertThat(hit.getExplanation().getValue().floatValue(), equalTo(hit.getScore())); + assertThat(hit.getExplanation().getDescription(), equalTo("rescored using LTR model ltr-model")); + } + } + ); + } + } + + private void createLtrIndex(int numberOfShards, int numberOfReplicas) { + client().admin() + .indices() + .prepareCreate(LTR_SEARCH_INDEX) + .setSettings( + Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, numberOfShards) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, numberOfReplicas) + .build() + ) + .setMapping("product", "type=keyword", "best_seller", "type=boolean") + .get(); + + BulkRequestBuilder bulkRequestBuilder = client().prepareBulk(); + IndexRequest indexRequest = new IndexRequest(LTR_SEARCH_INDEX); + indexRequest.source("product", "TV", "best_seller", true); + bulkRequestBuilder.add(indexRequest); + + indexRequest = new IndexRequest(LTR_SEARCH_INDEX); + indexRequest.source("product", "TV", "best_seller", false); + bulkRequestBuilder.add(indexRequest); + + indexRequest = new IndexRequest(LTR_SEARCH_INDEX); + indexRequest.source("product", "VCR", "best_seller", true); + bulkRequestBuilder.add(indexRequest); + + indexRequest = new IndexRequest(LTR_SEARCH_INDEX); + indexRequest.source("product", "VCR", "best_seller", true); + bulkRequestBuilder.add(indexRequest); + + indexRequest = new IndexRequest(LTR_SEARCH_INDEX); + indexRequest.source("product", "Laptop", "best_seller", true); + bulkRequestBuilder.add(indexRequest); + + BulkResponse bulkResponse = bulkRequestBuilder.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE).get(); + assertThat(bulkResponse.hasFailures(), is(false)); + } + + private void createLtrModel() throws IOException { + client().execute( + PutTrainedModelAction.INSTANCE, + new PutTrainedModelAction.Request( + TrainedModelConfig.builder() + .setModelId(LTR_MODEL) + .setInferenceConfig( + LearningToRankConfig.builder(LearningToRankConfig.EMPTY_PARAMS) + .setLearningToRankFeatureExtractorBuilders( + List.of( + new QueryExtractorBuilder( + "best_seller", + QueryProvider.fromParsedQuery(QueryBuilders.termQuery("best_seller", "true")) + ), + new QueryExtractorBuilder( + "product_match", + QueryProvider.fromParsedQuery(QueryBuilders.termQuery("product", "{{keyword}}")) + ) + ) + ) + .build() + ) + .setParsedDefinition( + new TrainedModelDefinition.Builder().setPreProcessors(Collections.emptyList()) + .setTrainedModel( + Ensemble.builder() + .setFeatureNames(List.of("best_seller", "product_bm25")) + .setTargetType(TargetType.REGRESSION) + .setTrainedModels( + List.of( + Tree.builder() + .setFeatureNames(List.of("best_seller")) + .setTargetType(TargetType.REGRESSION) + .setRoot( + TreeNode.builder(0) + .setSplitFeature(0) + .setSplitGain(12d) + .setThreshold(1d) + .setOperator(Operator.GTE) + .setDefaultLeft(true) + .setLeftChild(1) + .setRightChild(2) + ) + .addLeaf(1, 1) + .addLeaf(2, 5) + .build(), + Tree.builder() + .setFeatureNames(List.of("product_match")) + .setTargetType(TargetType.REGRESSION) + .setRoot( + TreeNode.builder(0) + .setSplitFeature(0) + .setSplitGain(12d) + .setThreshold(1d) + .setOperator(Operator.LT) + .setDefaultLeft(true) + .setLeftChild(1) + .setRightChild(2) + ) + .addLeaf(1, 10) + .addLeaf(2, 1) + .build() + ) + ) + .build() + ) + ) + .validate(true) + .build(), + false + ) + ).actionGet(); + } +} diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MlInitializationService.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MlInitializationService.java index 99df16bcd3dc2..a21f59f11540f 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MlInitializationService.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MlInitializationService.java @@ -155,7 +155,7 @@ public void clusterChanged(ClusterChangedEvent event) { AnnotationIndex.createAnnotationsIndexIfNecessary( client, event.state(), - MasterNodeRequest.TRAPPY_IMPLICIT_DEFAULT_MASTER_NODE_TIMEOUT, + MachineLearning.HARD_CODED_MACHINE_LEARNING_MASTER_NODE_TIMEOUT, ActionListener.wrap(r -> isIndexCreationInProgress.set(false), e -> { if (e.getMessage().equals(previousException)) { logger.debug("Error creating ML annotations index or aliases", e); diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/TrainedModelStatsService.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/TrainedModelStatsService.java index 4ee294bcf0d8c..67f2ea74464d0 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/TrainedModelStatsService.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/TrainedModelStatsService.java @@ -14,7 +14,6 @@ import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.action.support.PlainActionFuture; import org.elasticsearch.action.support.WriteRequest; -import org.elasticsearch.action.support.master.MasterNodeRequest; import org.elasticsearch.action.update.UpdateRequest; import org.elasticsearch.client.internal.OriginSettingClient; import org.elasticsearch.cluster.ClusterChangedEvent; @@ -256,14 +255,14 @@ private void createStatsIndexIfNecessary() { client, clusterState, indexNameExpressionResolver, - MasterNodeRequest.TRAPPY_IMPLICIT_DEFAULT_MASTER_NODE_TIMEOUT, + MachineLearning.HARD_CODED_MACHINE_LEARNING_MASTER_NODE_TIMEOUT, ActionListener.wrap( r -> ElasticsearchMappings.addDocMappingIfMissing( MlStatsIndex.writeAlias(), MlStatsIndex::wrappedMapping, client, clusterState, - MasterNodeRequest.TRAPPY_IMPLICIT_DEFAULT_MASTER_NODE_TIMEOUT, + MachineLearning.HARD_CODED_MACHINE_LEARNING_MASTER_NODE_TIMEOUT, listener, MlStatsIndex.STATS_INDEX_MAPPINGS_VERSION ), diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/ltr/QueryFeatureExtractor.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/ltr/QueryFeatureExtractor.java index bbc377a67ec0b..08c141c0858ca 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/ltr/QueryFeatureExtractor.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/ltr/QueryFeatureExtractor.java @@ -55,11 +55,16 @@ public void setNextReader(LeafReaderContext segmentContext) throws IOException { } scorers.add(scorer); } - rankerIterator = new DisjunctionDISIApproximation(disiPriorityQueue); + + rankerIterator = disiPriorityQueue.size() > 0 ? new DisjunctionDISIApproximation(disiPriorityQueue) : null; } @Override public void addFeatures(Map featureMap, int docId) throws IOException { + if (rankerIterator == null) { + return; + } + rankerIterator.advance(docId); for (int i = 0; i < featureNames.size(); i++) { Scorer scorer = scorers.get(i); diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/LocalStateMachineLearning.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/LocalStateMachineLearning.java index ff1a1d19779df..426716d0399c5 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/LocalStateMachineLearning.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/LocalStateMachineLearning.java @@ -95,6 +95,16 @@ protected SSLService getSslService() { }); } + @Override + public List> getQueries() { + return mlPlugin.getQueries(); + } + + @Override + public List> getRescorers() { + return mlPlugin.getRescorers(); + } + @Override public List getAggregations() { return mlPlugin.getAggregations(); diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/ingest/InferenceProcessorTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/ingest/InferenceProcessorTests.java index 6b0b589ace606..248e15f066fdb 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/ingest/InferenceProcessorTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/ingest/InferenceProcessorTests.java @@ -298,12 +298,13 @@ public void testGenerateRequestWithEmptyMapping() { Map source = new HashMap<>() { { + put("_version", 345); put("value1", 1); put("value2", 4); put("categorical", "foo"); } }; - IngestDocument document = TestIngestDocument.ofIngestWithNullableVersion(source, new HashMap<>()); + IngestDocument document = TestIngestDocument.withDefaultVersion(source, new HashMap<>()); var request = processor.buildRequest(document); assertThat(request.getObjectsToInfer().get(0), equalTo(source)); @@ -311,7 +312,7 @@ public void testGenerateRequestWithEmptyMapping() { assertEquals(TrainedModelPrefixStrings.PrefixType.INGEST, request.getPrefixType()); Map ingestMetadata = Collections.singletonMap("_value", 3); - document = TestIngestDocument.ofIngestWithNullableVersion(source, ingestMetadata); + document = TestIngestDocument.withDefaultVersion(source, ingestMetadata); Map expected = new HashMap<>(source); expected.put("_ingest", ingestMetadata); @@ -344,12 +345,14 @@ public void testGenerateWithMapping() { ); Map source = Maps.newMapWithExpectedSize(3); + source.put("_version", 234); source.put("value1", 1); source.put("categorical", "foo"); source.put("un_touched", "bar"); - IngestDocument document = TestIngestDocument.withNullableVersion(source); + IngestDocument document = TestIngestDocument.withDefaultVersion(source); Map expectedMap = Maps.newMapWithExpectedSize(5); + expectedMap.put("_version", 234); expectedMap.put("new_value1", 1); expectedMap.put("value1", 1); expectedMap.put("categorical", "foo"); @@ -361,7 +364,7 @@ public void testGenerateWithMapping() { assertEquals(TrainedModelPrefixStrings.PrefixType.INGEST, request.getPrefixType()); Map ingestMetadata = Collections.singletonMap("_value", "baz"); - document = TestIngestDocument.ofIngestWithNullableVersion(source, ingestMetadata); + document = TestIngestDocument.withDefaultVersion(source, ingestMetadata); expectedMap = new HashMap<>(expectedMap); expectedMap.put("metafield", "baz"); expectedMap.put("_ingest", ingestMetadata); @@ -392,12 +395,14 @@ public void testGenerateWithMappingNestedFields() { ); Map source = Maps.newMapWithExpectedSize(3); + source.put("_version", 987); source.put("value1", Collections.singletonMap("foo", 1)); source.put("categorical.bar", "foo"); source.put("un_touched", "bar"); - IngestDocument document = TestIngestDocument.withNullableVersion(source); + IngestDocument document = TestIngestDocument.withDefaultVersion(source); Map expectedMap = Maps.newMapWithExpectedSize(5); + expectedMap.put("_version", 987); expectedMap.put("new_value1", 1); expectedMap.put("value1", Collections.singletonMap("foo", 1)); expectedMap.put("categorical.bar", "foo"); diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/ltr/QueryFeatureExtractorTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/ltr/QueryFeatureExtractorTests.java index 3878ce5dab087..3b25a266bf412 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/ltr/QueryFeatureExtractorTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/ltr/QueryFeatureExtractorTests.java @@ -23,6 +23,7 @@ import org.elasticsearch.index.query.QueryRewriteContext; import org.elasticsearch.index.query.SearchExecutionContext; import org.elasticsearch.test.AbstractBuilderTestCase; +import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.core.ml.inference.trainedmodel.ltr.QueryExtractorBuilder; import org.elasticsearch.xpack.core.ml.utils.QueryProvider; @@ -31,12 +32,14 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Stream; import static org.hamcrest.Matchers.anEmptyMap; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.not; +import static org.mockito.Mockito.mock; public class QueryFeatureExtractorTests extends AbstractBuilderTestCase { @@ -125,4 +128,29 @@ public void testQueryExtractor() throws IOException { dir.close(); } + public void testEmptyDisiPriorityQueue() throws IOException { + addDocs( + new String[] { "the quick brown fox", "the slow brown fox", "the grey dog", "yet another string" }, + new int[] { 5, 10, 12, 11 } + ); + + // Scorers returned by weights are null + List featureNames = randomList(1, 10, ESTestCase::randomIdentifier); + List weights = Stream.generate(() -> mock(Weight.class)).limit(featureNames.size()).toList(); + + QueryFeatureExtractor featureExtractor = new QueryFeatureExtractor(featureNames, weights); + + for (LeafReaderContext leafReaderContext : searcher.getLeafContexts()) { + int maxDoc = leafReaderContext.reader().maxDoc(); + featureExtractor.setNextReader(leafReaderContext); + for (int i = 0; i < maxDoc; i++) { + Map featureMap = new HashMap<>(); + featureExtractor.addFeatures(featureMap, i); + assertThat(featureMap, anEmptyMap()); + } + } + + reader.close(); + dir.close(); + } } diff --git a/x-pack/plugin/profiling/src/internalClusterTest/resources/data/profiling-hosts.ndjson b/x-pack/plugin/profiling/src/internalClusterTest/resources/data/profiling-hosts.ndjson index e12a670a79d18..8cd5425c86994 100644 --- a/x-pack/plugin/profiling/src/internalClusterTest/resources/data/profiling-hosts.ndjson +++ b/x-pack/plugin/profiling/src/internalClusterTest/resources/data/profiling-hosts.ndjson @@ -1,4 +1,4 @@ {"create": {"_index": "profiling-hosts","_id":"eLH27YsBj2lLi3tJYlvr"}} -{"profiling.project.id":100,"host.id":"8457605156473051743","@timestamp":1700504426,"ecs.version":"1.12.0","profiling.agent.build_timestamp":1688111067,"profiling.instance.private_ipv4s":["192.168.1.2"],"ec2.instance_life_cycle":"on-demand","profiling.agent.config.map_scale_factor":0,"host.type":"i3.2xlarge","profiling.host.ip":"192.168.1.2","profiling.agent.config.bpf_log_level":0,"profiling.host.sysctl.net.core.bpf_jit_enable":1,"profiling.agent.config.file":"/etc/prodfiler/prodfiler.conf","ec2.local_ipv4":"192.168.1.2","profiling.agent.config.no_kernel_version_check":false,"host.arch":"amd64","profiling.host.tags":["cloud_provider:aws","cloud_environment:qa","cloud_region:eu-west-1"],"profiling.agent.config.probabilistic_threshold":100,"profiling.agent.config.disable_tls":false,"profiling.agent.config.tracers":"all","profiling.agent.start_time":1700090045589,"profiling.agent.config.max_elements_per_interval":800,"cloud.provider":"aws","cloud.region":"eu-west-1","profiling.agent.config.present_cpu_cores":8,"profiling.host.kernel_version":"9.9.9-0-aws","profiling.agent.config.bpf_log_size":65536,"profiling.agent.config.known_traces_entries":65536,"profiling.host.sysctl.kernel.unprivileged_bpf_disabled":1,"profiling.agent.config.verbose":false,"profiling.agent.config.probabilistic_interval":"1m0s","ec2.placement.availability_zone_id":"euw1-az1","ec2.security_groups":"","ec2.local_hostname":"ip-192-168-1-2.eu-west-1.compute.internal","ec2.placement.availability_zone":"eu-west-1c","profiling.agent.config.upload_symbols":false,"profiling.host.sysctl.kernel.bpf_stats_enabled":0,"profiling.host.name":"ip-192-168-1-2","ec2.mac":"00:11:22:33:44:55","profiling.host.kernel_proc_version":"Linux version 9.9.9-0-aws","profiling.agent.config.cache_directory":"/var/cache/optimyze/","profiling.agent.version":"v8.12.0","ec2.hostname":"ip-192-168-1-2.eu-west-1.compute.internal","profiling.agent.config.elastic_mode":false,"ec2.ami_id":"ami-aaaaaaaaaaa","ec2.instance_id":"i-0b999999999999999"} +{"profiling.project.id":100,"host.id":"8457605156473051743","@timestamp":1700504426,"ecs.version":"1.12.0","profiling.agent.build_timestamp":1688111067,"profiling.instance.private_ipv4s":["192.168.1.2"],"ec2.instance_life_cycle":"on-demand","profiling.agent.config.map_scale_factor":0,"host.type":"i3.2xlarge","profiling.host.ip":"192.168.1.2","profiling.agent.config.bpf_log_level":0,"profiling.host.sysctl.net.core.bpf_jit_enable":1,"profiling.agent.config.file":"/etc/prodfiler/prodfiler.conf","ec2.local_ipv4":"192.168.1.2","profiling.agent.config.no_kernel_version_check":false,"host.arch":"amd64","profiling.host.tags":["cloud_provider:aws","cloud_environment:qa","cloud_region:eu-west-1"],"profiling.agent.config.probabilistic_threshold":100,"profiling.agent.config.disable_tls":false,"profiling.agent.config.tracers":"all","profiling.agent.start_time":1700090045589,"profiling.agent.config.max_elements_per_interval":800,"cloud.provider":"aws","cloud.region":"eu-west-1","profiling.agent.config.present_cpu_cores":8,"profiling.host.kernel_version":"9.9.9-0-aws","profiling.agent.config.bpf_log_size":65536,"profiling.agent.config.known_traces_entries":65536,"profiling.host.sysctl.kernel.unprivileged_bpf_disabled":1,"profiling.agent.config.verbose":false,"profiling.agent.config.probabilistic_interval":"1m0s","ec2.placement.availability_zone_id":"euw1-az1","ec2.security_groups":"","ec2.local_hostname":"ip-192-168-1-2.eu-west-1.compute.internal","ec2.placement.availability_zone":"eu-west-1c","profiling.agent.config.upload_symbols":false,"profiling.host.sysctl.kernel.bpf_stats_enabled":0,"host.name":"ip-192-168-1-2","ec2.mac":"00:11:22:33:44:55","profiling.host.kernel_proc_version":"Linux version 9.9.9-0-aws","profiling.agent.config.cache_directory":"/var/cache/optimyze/","profiling.agent.version":"v8.12.0","ec2.hostname":"ip-192-168-1-2.eu-west-1.compute.internal","profiling.agent.config.elastic_mode":false,"ec2.ami_id":"ami-aaaaaaaaaaa","ec2.instance_id":"i-0b999999999999999"} {"create": {"_index": "profiling-hosts", "_id": "u_fHlYwBkmZvQ6tVo1Lr"}} -{"profiling.project.id":100,"host.id":"7416508186220657211","@timestamp":1703319912,"ecs.version":"1.12.0","profiling.agent.version":"8.11.0","profiling.agent.config.map_scale_factor":0,"profiling.agent.config.probabilistic_threshold":100,"profiling.host.name":"ip-192-186-1-3","profiling.agent.config.no_kernel_version_check":false,"profiling.host.sysctl.net.core.bpf_jit_enable":1,"profiling.agent.config.elastic_mode":false,"host.type":"Standard_D4s_v3","azure.compute.environment":"AzurePublicCloud","profiling.agent.config.bpf_log_level":0,"profiling.agent.config.known_traces_entries":65536,"profiling.agent.config.ca_address":"example.com:443","profiling.agent.config.tags":"cloud_provider:azure;cloud_environment:qa;cloud_region:eastus2","profiling.host.tags":["cloud_provider:azure","cloud_environment:qa","cloud_region:eastus2"],"profiling.host.kernel_version":"9.9.9-0-azure","profiling.agent.revision":"head-52cc2030","azure.compute.subscriptionid":"1-2-3-4-5","profiling.host.sysctl.kernel.bpf_stats_enabled":0,"host.arch":"amd64","azure.compute.zone":"3","profiling.agent.config.cache_directory":"/var/cache/Elastic/universal-profiling","azure.compute.name":"example-qa-eastus2-001-v1-zone3_6","profiling.agent.config.probabilistic_interval":"1m0s","cloud.provider":"azure","cloud.region":"eastus2","azure.compute.version":"1234.20230510.233254","profiling.instance.private_ipv4s":["192.168.1.3"],"profiling.agent.build_timestamp":1699000836,"profiling.agent.config.file":"/etc/Elastic/universal-profiling/pf-host-agent.conf","profiling.agent.config.bpf_log_size":65536,"profiling.host.sysctl.kernel.unprivileged_bpf_disabled":1,"profiling.agent.config.tracers":"all","profiling.agent.config.present_cpu_cores":4,"profiling.agent.start_time":1702306987358,"profiling.agent.config.disable_tls":false,"azure.compute.ostype":"Linux","profiling.host.ip":"192.168.1.3","profiling.agent.config.max_elements_per_interval":400,"profiling.agent.config.upload_symbols":false,"azure.compute.tags":"bootstrap-version:v1;ece-id:001;environment:qa;identifier:v1;initial-config:;managed-by:terraform;monitored-by:core-infrastructure;owner:core-infrastructure;region_type:ess;role:blueprint;secondary_role:;vars-identifier:eastus2-001-v1","profiling.host.kernel_proc_version":"Linux version 9.9.9-0-azure","profiling.agent.config.verbose":false,"azure.compute.vmid":"1-2-3-4-5"} +{"profiling.project.id":100,"host.id":"7416508186220657211","@timestamp":1703319912,"ecs.version":"1.12.0","profiling.agent.version":"8.11.0","profiling.agent.config.map_scale_factor":0,"profiling.agent.config.probabilistic_threshold":100,"host.name":"ip-192-186-1-3","profiling.agent.config.no_kernel_version_check":false,"profiling.host.sysctl.net.core.bpf_jit_enable":1,"profiling.agent.config.elastic_mode":false,"host.type":"Standard_D4s_v3","azure.compute.environment":"AzurePublicCloud","profiling.agent.config.bpf_log_level":0,"profiling.agent.config.known_traces_entries":65536,"profiling.agent.config.ca_address":"example.com:443","profiling.agent.config.tags":"cloud_provider:azure;cloud_environment:qa;cloud_region:eastus2","profiling.host.tags":["cloud_provider:azure","cloud_environment:qa","cloud_region:eastus2"],"profiling.host.kernel_version":"9.9.9-0-azure","profiling.agent.revision":"head-52cc2030","azure.compute.subscriptionid":"1-2-3-4-5","profiling.host.sysctl.kernel.bpf_stats_enabled":0,"host.arch":"amd64","azure.compute.zone":"3","profiling.agent.config.cache_directory":"/var/cache/Elastic/universal-profiling","azure.compute.name":"example-qa-eastus2-001-v1-zone3_6","profiling.agent.config.probabilistic_interval":"1m0s","cloud.provider":"azure","cloud.region":"eastus2","azure.compute.version":"1234.20230510.233254","profiling.instance.private_ipv4s":["192.168.1.3"],"profiling.agent.build_timestamp":1699000836,"profiling.agent.config.file":"/etc/Elastic/universal-profiling/pf-host-agent.conf","profiling.agent.config.bpf_log_size":65536,"profiling.host.sysctl.kernel.unprivileged_bpf_disabled":1,"profiling.agent.config.tracers":"all","profiling.agent.config.present_cpu_cores":4,"profiling.agent.start_time":1702306987358,"profiling.agent.config.disable_tls":false,"azure.compute.ostype":"Linux","profiling.host.ip":"192.168.1.3","profiling.agent.config.max_elements_per_interval":400,"profiling.agent.config.upload_symbols":false,"azure.compute.tags":"bootstrap-version:v1;ece-id:001;environment:qa;identifier:v1;initial-config:;managed-by:terraform;monitored-by:core-infrastructure;owner:core-infrastructure;region_type:ess;role:blueprint;secondary_role:;vars-identifier:eastus2-001-v1","profiling.host.kernel_proc_version":"Linux version 9.9.9-0-azure","profiling.agent.config.verbose":false,"azure.compute.vmid":"1-2-3-4-5"} diff --git a/x-pack/plugin/security/qa/multi-cluster/src/javaRestTest/java/org/elasticsearch/xpack/remotecluster/CrossClusterEsqlRCS1UnavailableRemotesIT.java b/x-pack/plugin/security/qa/multi-cluster/src/javaRestTest/java/org/elasticsearch/xpack/remotecluster/CrossClusterEsqlRCS1UnavailableRemotesIT.java index c7623779ee214..9759b1440c3e8 100644 --- a/x-pack/plugin/security/qa/multi-cluster/src/javaRestTest/java/org/elasticsearch/xpack/remotecluster/CrossClusterEsqlRCS1UnavailableRemotesIT.java +++ b/x-pack/plugin/security/qa/multi-cluster/src/javaRestTest/java/org/elasticsearch/xpack/remotecluster/CrossClusterEsqlRCS1UnavailableRemotesIT.java @@ -26,6 +26,7 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.greaterThan; public class CrossClusterEsqlRCS1UnavailableRemotesIT extends AbstractRemoteClusterSecurityTestCase { @@ -178,7 +179,14 @@ private void remoteClusterShutdownWithSkipUnavailableFalse() throws Exception { // A simple query that targets our remote cluster. String query = "FROM *,my_remote_cluster:* | LIMIT 10"; ResponseException ex = expectThrows(ResponseException.class, () -> client().performRequest(esqlRequest(query))); - assertThat(ex.getMessage(), containsString("connect_transport_exception")); + assertThat( + ex.getMessage(), + anyOf( + containsString("connect_transport_exception"), + containsString("node_disconnected_exception"), + containsString("node_not_connected_exception") + ) + ); } finally { fulfillingCluster.start(); closeFulfillingClusterClient(); diff --git a/x-pack/plugin/security/qa/multi-cluster/src/javaRestTest/java/org/elasticsearch/xpack/remotecluster/CrossClusterEsqlRCS2UnavailableRemotesIT.java b/x-pack/plugin/security/qa/multi-cluster/src/javaRestTest/java/org/elasticsearch/xpack/remotecluster/CrossClusterEsqlRCS2UnavailableRemotesIT.java index b62d82c47f753..50bd386b2172c 100644 --- a/x-pack/plugin/security/qa/multi-cluster/src/javaRestTest/java/org/elasticsearch/xpack/remotecluster/CrossClusterEsqlRCS2UnavailableRemotesIT.java +++ b/x-pack/plugin/security/qa/multi-cluster/src/javaRestTest/java/org/elasticsearch/xpack/remotecluster/CrossClusterEsqlRCS2UnavailableRemotesIT.java @@ -25,10 +25,12 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicReference; +import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.oneOf; public class CrossClusterEsqlRCS2UnavailableRemotesIT extends AbstractRemoteClusterSecurityTestCase { private static final AtomicReference> API_KEY_MAP_REF = new AtomicReference<>(); @@ -179,8 +181,10 @@ private void remoteClusterShutdownWithSkipUnavailableTrue() throws Exception { Map failuresMap = (Map) remoteClusterFailures.get(0); Map reason = (Map) failuresMap.get("reason"); - assertThat(reason.get("type").toString(), equalTo("connect_transport_exception")); - assertThat(reason.get("reason").toString(), containsString("Unable to connect to [my_remote_cluster]")); + assertThat( + reason.get("type").toString(), + oneOf("node_disconnected_exception", "connect_transport_exception", "node_not_connected_exception") + ); } finally { fulfillingCluster.start(); closeFulfillingClusterClient(); @@ -201,7 +205,14 @@ private void remoteClusterShutdownWithSkipUnavailableFalse() throws Exception { // A simple query that targets our remote cluster. String query = "FROM *,my_remote_cluster:* | LIMIT 10"; ResponseException ex = expectThrows(ResponseException.class, () -> performRequestWithRemoteSearchUser(esqlRequest(query))); - assertThat(ex.getMessage(), containsString("connect_transport_exception")); + assertThat( + ex.getMessage(), + anyOf( + containsString("node_disconnected_exception"), + containsString("connect_transport_exception"), + containsString("node_not_connected_exception") + ) + ); } finally { fulfillingCluster.start(); closeFulfillingClusterClient(); diff --git a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/esql/190_lookup_join.yml b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/esql/190_lookup_join.yml index 16125df6384c3..f72cdd65b275c 100644 --- a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/esql/190_lookup_join.yml +++ b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/esql/190_lookup_join.yml @@ -6,7 +6,7 @@ setup: - method: POST path: /_query parameters: [] - capabilities: [join_lookup_v11] + capabilities: [join_lookup_v12] reason: "uses LOOKUP JOIN" - do: indices.create: @@ -81,7 +81,7 @@ basic: - do: esql.query: body: - query: 'FROM test | SORT key | LOOKUP JOIN `test-lookup-1` ON key | LIMIT 3' + query: 'FROM test | SORT key | LOOKUP JOIN test-lookup-1 ON key | LIMIT 3' - match: {columns.0.name: "key"} - match: {columns.0.type: "long"} @@ -95,18 +95,21 @@ non-lookup index: - do: esql.query: body: - query: 'FROM test-lookup-1 | SORT key | LOOKUP JOIN `test` ON key | LIMIT 3' + query: 'FROM test-lookup-1 | SORT key | LOOKUP JOIN test ON key | LIMIT 3' catch: "bad_request" - match: { error.type: "verification_exception" } - contains: { error.reason: "Found 1 problem\nline 1:45: invalid [test] resolution in lookup mode to an index in [standard] mode" } --- + "Alias as lookup index": + - skip: + awaits_fix: "LOOKUP JOIN does not support index aliases for now" - do: esql.query: body: - query: 'FROM test | SORT key | LOOKUP JOIN `test-lookup-alias` ON key | LIMIT 3' + query: 'FROM test | SORT key | LOOKUP JOIN test-lookup-alias ON key | LIMIT 3' - match: {columns.0.name: "key"} - match: {columns.0.type: "long"} @@ -117,10 +120,12 @@ non-lookup index: --- alias-repeated-alias: + - skip: + awaits_fix: "LOOKUP JOIN does not support index aliases for now" - do: esql.query: body: - query: 'FROM test-lookup-alias | SORT key | LOOKUP JOIN `test-lookup-alias` ON key | LIMIT 3' + query: 'FROM test-lookup-alias | SORT key | LOOKUP JOIN test-lookup-alias ON key | LIMIT 3' - match: {columns.0.name: "key"} - match: {columns.0.type: "long"} @@ -131,10 +136,12 @@ alias-repeated-alias: --- alias-repeated-index: + - skip: + awaits_fix: "LOOKUP JOIN does not support index aliases for now" - do: esql.query: body: - query: 'FROM test-lookup-1 | SORT key | LOOKUP JOIN `test-lookup-alias` ON key | LIMIT 3' + query: 'FROM test-lookup-1 | SORT key | LOOKUP JOIN test-lookup-alias ON key | LIMIT 3' - match: {columns.0.name: "key"} - match: {columns.0.type: "long"} @@ -145,10 +152,12 @@ alias-repeated-index: --- alias-pattern-multiple: + - skip: + awaits_fix: "LOOKUP JOIN does not support index aliases for now" - do: esql.query: body: - query: 'FROM test-lookup-1 | LOOKUP JOIN `test-lookup-alias-pattern-multiple` ON key' + query: 'FROM test-lookup-1 | LOOKUP JOIN test-lookup-alias-pattern-multiple ON key' catch: "bad_request" - match: { error.type: "verification_exception" } @@ -156,10 +165,12 @@ alias-pattern-multiple: --- alias-pattern-single: + - skip: + awaits_fix: "LOOKUP JOIN does not support index aliases for now" - do: esql.query: body: - query: 'FROM test | SORT key | LOOKUP JOIN `test-lookup-alias-pattern-single` ON key | LIMIT 3' + query: 'FROM test | SORT key | LOOKUP JOIN test-lookup-alias-pattern-single ON key | LIMIT 3' - match: {columns.0.name: "key"} - match: {columns.0.type: "long"} @@ -167,3 +178,25 @@ alias-pattern-single: - match: {columns.1.type: "keyword"} - match: {values.0: [1, "cyan"]} - match: {values.1: [2, "yellow"]} + +--- +pattern-multiple: + - do: + esql.query: + body: + query: 'FROM test-lookup-1 | LOOKUP JOIN test-lookup-* ON key' + catch: "bad_request" + + - match: { error.type: "parsing_exception" } + - contains: { error.reason: "line 1:34: invalid index pattern [test-lookup-*], * is not allowed in LOOKUP JOIN" } + +--- +pattern-single: + - do: + esql.query: + body: + query: 'FROM test | SORT key | LOOKUP JOIN test-lookup-1* ON key | LIMIT 3' + catch: "bad_request" + + - match: { error.type: "parsing_exception" } + - contains: { error.reason: "line 1:36: invalid index pattern [test-lookup-1*], * is not allowed in LOOKUP JOIN" } diff --git a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/esql/191_lookup_join_on_datastreams.yml b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/esql/191_lookup_join_on_datastreams.yml new file mode 100644 index 0000000000000..6f9b70b0d94f1 --- /dev/null +++ b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/esql/191_lookup_join_on_datastreams.yml @@ -0,0 +1,68 @@ +--- +setup: + - requires: + test_runner_features: [capabilities, contains] + capabilities: + - method: POST + path: /_query + parameters: [] + capabilities: [lookup_join_no_aliases] + reason: "uses LOOKUP JOIN" + + - do: + cluster.put_component_template: + name: my_settings + body: + template: + settings: + index: + mode: lookup + + + - do: + cluster.put_component_template: + name: my_mappings + body: + template: + mappings: + properties: + "@timestamp": + type: date + x: + type: keyword + + - do: + indices.put_index_template: + name: my_index_template + body: + index_patterns: my_data_stream* + data_stream: {} + composed_of: [ "my_mappings", "my_settings" ] + priority: 500 + + + - do: + bulk: + index: "my_data_stream" + refresh: true + body: + - { "index": { } } + - { "x": "foo", "y": "y1" } + - { "index": { } } + - { "x": "bar", "y": "y2" } + + + +--- +"data streams not supported in LOOKUP JOIN": + - do: + esql.query: + body: + query: 'row x = "foo" | LOOKUP JOIN my_data_stream ON x' + catch: "bad_request" + + - match: { error.type: "verification_exception" } + - contains: { error.reason: "Found 1 problem\nline 1:17: Aliases and index patterns are not allowed for LOOKUP JOIN [my_data_stream]" } + + + diff --git a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/esql/191_lookup_join_text.yml b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/esql/191_lookup_join_text.yml index 7d1b3a90c6081..ab1ab0ff5d88a 100644 --- a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/esql/191_lookup_join_text.yml +++ b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/esql/191_lookup_join_text.yml @@ -6,7 +6,7 @@ setup: - method: POST path: /_query parameters: [] - capabilities: [lookup_join_text, join_lookup_v11] + capabilities: [lookup_join_text, join_lookup_v12] reason: "uses LOOKUP JOIN" - do: indices.create: @@ -69,7 +69,7 @@ keyword-keyword: - do: esql.query: body: - query: 'FROM test | SORT color | LOOKUP JOIN `test-lookup` ON color.keyword | LIMIT 3' + query: 'FROM test | SORT color | LOOKUP JOIN test-lookup ON color.keyword | LIMIT 3' - length: { columns: 4 } - length: { values: 3 } @@ -90,7 +90,7 @@ text-keyword: - do: esql.query: body: - query: 'FROM test | SORT color | RENAME color AS x | EVAL color.keyword = x | LOOKUP JOIN `test-lookup` ON color.keyword | LIMIT 3' + query: 'FROM test | SORT color | RENAME color AS x | EVAL color.keyword = x | LOOKUP JOIN test-lookup ON color.keyword | LIMIT 3' - length: { columns: 5 } - length: { values: 3 } @@ -113,20 +113,20 @@ text-text: - do: esql.query: body: - query: 'FROM test | SORT color | LOOKUP JOIN `test-lookup` ON color | LIMIT 3' + query: 'FROM test | SORT color | LOOKUP JOIN test-lookup ON color | LIMIT 3' catch: "bad_request" - match: { error.type: "verification_exception" } - - contains: { error.reason: "Found 1 problem\nline 1:55: JOIN with right field [color] of type [TEXT] is not supported" } + - contains: { error.reason: "Found 1 problem\nline 1:53: JOIN with right field [color] of type [TEXT] is not supported" } --- keyword-text: - do: esql.query: body: - query: 'FROM test | SORT color | EVAL color = color.keyword | LOOKUP JOIN `test-lookup` ON color | LIMIT 3' + query: 'FROM test | SORT color | EVAL color = color.keyword | LOOKUP JOIN test-lookup ON color | LIMIT 3' catch: "bad_request" - match: { error.type: "verification_exception" } - - contains: { error.reason: "Found 1 problem\nline 1:84: JOIN with right field [color] of type [TEXT] is not supported" } + - contains: { error.reason: "Found 1 problem\nline 1:82: JOIN with right field [color] of type [TEXT] is not supported" } diff --git a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/migrate/10_reindex.yml b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/migrate/10_reindex.yml index 247f49efb5404..df6feb1502dc4 100644 --- a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/migrate/10_reindex.yml +++ b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/migrate/10_reindex.yml @@ -7,7 +7,7 @@ setup: --- "Test Reindex With Unsupported Mode": - requires: - reason: "migration reindex is behind a feature flag" + reason: "requires a cluster with the migration reindex feature" test_runner_features: [capabilities] capabilities: - method: POST @@ -27,7 +27,7 @@ setup: --- "Test Reindex With Nonexistent Data Stream": - requires: - reason: "migration reindex is behind a feature flag" + reason: "requires a cluster with the migration reindex feature" test_runner_features: [capabilities] capabilities: - method: POST @@ -59,7 +59,7 @@ setup: --- "Test Reindex With Bad Data Stream Name": - requires: - reason: "migration reindex is behind a feature flag" + reason: "requires a cluster with the migration reindex feature" test_runner_features: [capabilities] capabilities: - method: POST @@ -79,7 +79,7 @@ setup: --- "Test Reindex With Existing Data Stream": - requires: - reason: "migration reindex is behind a feature flag" + reason: "requires a cluster with the migration reindex feature" test_runner_features: [capabilities] capabilities: - method: POST diff --git a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/migrate/20_reindex_status.yml b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/migrate/20_reindex_status.yml index c65786a188687..616d320ae7ffe 100644 --- a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/migrate/20_reindex_status.yml +++ b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/migrate/20_reindex_status.yml @@ -7,7 +7,7 @@ setup: --- "Test get reindex status with nonexistent task id": - requires: - reason: "migration reindex is behind a feature flag" + reason: "requires a cluster with the migration reindex feature" test_runner_features: [capabilities] capabilities: - method: POST @@ -21,7 +21,7 @@ setup: --- "Test Reindex With Existing Data Stream": - requires: - reason: "migration reindex is behind a feature flag" + reason: "requires a cluster with the migration reindex feature" test_runner_features: [capabilities] capabilities: - method: POST diff --git a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/migrate/30_create_from.yml b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/migrate/30_create_from.yml index 269d66474b1d7..cb9b0d3fe896a 100644 --- a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/migrate/30_create_from.yml +++ b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/migrate/30_create_from.yml @@ -18,7 +18,7 @@ teardown: --- "Test create from with nonexistent source index": - requires: - reason: "migration reindex is behind a feature flag" + reason: "requires a cluster with the migration reindex feature" test_runner_features: [capabilities] capabilities: - method: POST @@ -33,7 +33,7 @@ teardown: --- "Test create_from with existing source index": - requires: - reason: "migration reindex is behind a feature flag" + reason: "requires a cluster with the migration reindex feature" test_runner_features: [capabilities] capabilities: - method: POST @@ -69,7 +69,7 @@ teardown: --- "Test create_from with existing source index and overrides": - requires: - reason: "migration reindex is behind a feature flag" + reason: "requires a cluster with the migration reindex feature" test_runner_features: [capabilities] capabilities: - method: POST @@ -122,7 +122,7 @@ teardown: --- "Test create_from with remove_index_blocks set to false": - requires: - reason: "migration reindex is behind a feature flag" + reason: "requires a cluster with the migration reindex feature" test_runner_features: [capabilities] capabilities: - method: POST @@ -155,7 +155,7 @@ teardown: --- "Test create_from with remove_index_blocks default of true": - requires: - reason: "migration reindex is behind a feature flag" + reason: "requires a cluster with the migration reindex feature" test_runner_features: [capabilities] capabilities: - method: POST diff --git a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/profiling/10_basic.yml b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/profiling/10_basic.yml index 4a9212b8a7158..7d93e7f6e08c7 100644 --- a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/profiling/10_basic.yml +++ b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/profiling/10_basic.yml @@ -116,7 +116,7 @@ setup: - {"create": {"_index": "profiling-executables", "_id": "lHp5_WAgpLy2alrUVab6HA"}} - {"@timestamp": "1698624000", "Executable": {"build": {"id": "c5f89ea1c68710d2a493bb604c343a92c4f8ddeb"}, "file": {"name": "vmlinux"}}, "Symbolization": {"next_time": "4852491791"}, "ecs": {"version": "1.12.0"}} - {"create": {"_index": "profiling-hosts", "_id": "eLH27YsBj2lLi3tJYlvr"}} - - {"profiling.project.id": 100, "host.id": "8457605156473051743", "@timestamp": 1700504426, "ecs.version": "1.12.0", "profiling.agent.build_timestamp": 1688111067, "profiling.instance.private_ipv4s": ["192.168.1.2"], "ec2.instance_life_cycle": "on-demand", "profiling.agent.config.map_scale_factor": 0, "host.type": "i3.2xlarge", "profiling.host.ip": "192.168.1.2", "profiling.agent.config.bpf_log_level": 0, "profiling.host.sysctl.net.core.bpf_jit_enable": 1, "profiling.agent.config.file": "/etc/prodfiler/prodfiler.conf", "ec2.local_ipv4": "192.168.1.2", "profiling.agent.config.no_kernel_version_check": false, "host.arch": "amd64", "profiling.host.tags": ["cloud_provider:aws", "cloud_environment:qa", "cloud_region:eu-west-1"], "profiling.agent.config.probabilistic_threshold": 100, "profiling.agent.config.disable_tls": false, "profiling.agent.config.tracers": "all", "profiling.agent.start_time": 1700090045589, "profiling.agent.config.max_elements_per_interval": 800, "cloud.provider": "aws", "cloud.region": "eu-west-1", "profiling.agent.config.present_cpu_cores": 8, "profiling.host.kernel_version": "9.9.9-0-aws", "profiling.agent.config.bpf_log_size": 65536, "profiling.agent.config.known_traces_entries": 65536, "profiling.host.sysctl.kernel.unprivileged_bpf_disabled": 1, "profiling.agent.config.verbose": false, "profiling.agent.config.probabilistic_interval": "1m0s", "ec2.placement.availability_zone_id": "euw1-az1", "ec2.security_groups": "", "ec2.local_hostname": "ip-192-168-1-2.eu-west-1.compute.internal", "ec2.placement.availability_zone": "eu-west-1c", "profiling.agent.config.upload_symbols": false, "profiling.host.sysctl.kernel.bpf_stats_enabled": 0, "profiling.host.name": "ip-192-168-1-2", "ec2.mac": "00:11:22:33:44:55", "profiling.host.kernel_proc_version": "Linux version 9.9.9-0-aws", "profiling.agent.config.cache_directory": "/var/cache/optimyze/", "profiling.agent.version": "v8.12.0", "ec2.hostname": "ip-192-168-1-2.eu-west-1.compute.internal", "profiling.agent.config.elastic_mode": false, "ec2.ami_id": "ami-aaaaaaaaaaa", "ec2.instance_id": "i-0b999999999999999" } + - {"profiling.project.id": 100, "host.id": "8457605156473051743", "@timestamp": 1700504426, "ecs.version": "1.12.0", "profiling.agent.build_timestamp": 1688111067, "profiling.instance.private_ipv4s": ["192.168.1.2"], "ec2.instance_life_cycle": "on-demand", "profiling.agent.config.map_scale_factor": 0, "host.type": "i3.2xlarge", "profiling.host.ip": "192.168.1.2", "profiling.agent.config.bpf_log_level": 0, "profiling.host.sysctl.net.core.bpf_jit_enable": 1, "profiling.agent.config.file": "/etc/prodfiler/prodfiler.conf", "ec2.local_ipv4": "192.168.1.2", "profiling.agent.config.no_kernel_version_check": false, "host.arch": "amd64", "profiling.host.tags": ["cloud_provider:aws", "cloud_environment:qa", "cloud_region:eu-west-1"], "profiling.agent.config.probabilistic_threshold": 100, "profiling.agent.config.disable_tls": false, "profiling.agent.config.tracers": "all", "profiling.agent.start_time": 1700090045589, "profiling.agent.config.max_elements_per_interval": 800, "cloud.provider": "aws", "cloud.region": "eu-west-1", "profiling.agent.config.present_cpu_cores": 8, "profiling.host.kernel_version": "9.9.9-0-aws", "profiling.agent.config.bpf_log_size": 65536, "profiling.agent.config.known_traces_entries": 65536, "profiling.host.sysctl.kernel.unprivileged_bpf_disabled": 1, "profiling.agent.config.verbose": false, "profiling.agent.config.probabilistic_interval": "1m0s", "ec2.placement.availability_zone_id": "euw1-az1", "ec2.security_groups": "", "ec2.local_hostname": "ip-192-168-1-2.eu-west-1.compute.internal", "ec2.placement.availability_zone": "eu-west-1c", "profiling.agent.config.upload_symbols": false, "profiling.host.sysctl.kernel.bpf_stats_enabled": 0, "host.name": "ip-192-168-1-2", "ec2.mac": "00:11:22:33:44:55", "profiling.host.kernel_proc_version": "Linux version 9.9.9-0-aws", "profiling.agent.config.cache_directory": "/var/cache/optimyze/", "profiling.agent.version": "v8.12.0", "ec2.hostname": "ip-192-168-1-2.eu-west-1.compute.internal", "profiling.agent.config.elastic_mode": false, "ec2.ami_id": "ami-aaaaaaaaaaa", "ec2.instance_id": "i-0b999999999999999" } - {"index": {"_index": "test-events"}} - {"@timestamp": "1700504427", "events": ["S07KmaoGhvNte78xwwRbZQ"]} --- diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/support/search/WatcherSearchTemplateRequest.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/support/search/WatcherSearchTemplateRequest.java index 15208f86a5e2b..f62830f2345ea 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/support/search/WatcherSearchTemplateRequest.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/support/search/WatcherSearchTemplateRequest.java @@ -12,7 +12,6 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; -import org.elasticsearch.common.logging.DeprecationCategory; import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.core.Nullable; import org.elasticsearch.script.Script; @@ -173,7 +172,6 @@ public static WatcherSearchTemplateRequest fromXContent(XContentParser parser, S IndicesOptions indicesOptions = DEFAULT_INDICES_OPTIONS; BytesReference searchSource = null; Script template = null; - // TODO this is to retain BWC compatibility in 7.0 and can be removed for 8.0 boolean totalHitsAsInt = true; XContentParser.Token token; @@ -196,17 +194,6 @@ public static WatcherSearchTemplateRequest fromXContent(XContentParser parser, S ); } } - } else if (TYPES_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { - // Tolerate an empty types array, because some watches created internally in 6.x have - // an empty types array in their search, and it's clearly equivalent to typeless. - if (parser.nextToken() != XContentParser.Token.END_ARRAY) { - throw new ElasticsearchParseException( - "could not read search request. unsupported non-empty array field [" + currentFieldName + "]" - ); - } - // Empty types arrays still generate the same deprecation warning they did in 7.x. - // Ideally they should be removed from the definition. - deprecationLogger.critical(DeprecationCategory.PARSING, "watcher_search_input", TYPES_DEPRECATION_MESSAGE); } else { throw new ElasticsearchParseException( "could not read search request. unexpected array field [" + currentFieldName + "]" @@ -289,7 +276,6 @@ public int hashCode() { } private static final ParseField INDICES_FIELD = new ParseField("indices"); - private static final ParseField TYPES_FIELD = new ParseField("types"); private static final ParseField BODY_FIELD = new ParseField("body"); private static final ParseField SEARCH_TYPE_FIELD = new ParseField("search_type"); private static final ParseField INDICES_OPTIONS_FIELD = new ParseField("indices_options"); diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/support/search/WatcherSearchTemplateRequestTests.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/support/search/WatcherSearchTemplateRequestTests.java index 620580ee09824..96d8201b37a15 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/support/search/WatcherSearchTemplateRequestTests.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/support/search/WatcherSearchTemplateRequestTests.java @@ -17,7 +17,6 @@ import java.util.Map; import static java.util.Collections.singletonMap; -import static org.hamcrest.Matchers.arrayContaining; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; @@ -52,8 +51,11 @@ public void testFromXContentWithEmptyTypes() throws IOException { """; try (XContentParser parser = createParser(JsonXContent.jsonXContent, source)) { parser.nextToken(); - WatcherSearchTemplateRequest result = WatcherSearchTemplateRequest.fromXContent(parser, randomFrom(SearchType.values())); - assertThat(result.getIndices(), arrayContaining(".ml-anomalies-*")); + ElasticsearchParseException e = expectThrows( + ElasticsearchParseException.class, + () -> WatcherSearchTemplateRequest.fromXContent(parser, randomFrom(SearchType.values())) + ); + assertThat(e.getMessage(), is("could not read search request. unexpected array field [types]")); } } @@ -74,7 +76,7 @@ public void testFromXContentWithNonEmptyTypes() throws IOException { ElasticsearchParseException.class, () -> WatcherSearchTemplateRequest.fromXContent(parser, randomFrom(SearchType.values())) ); - assertThat(e.getMessage(), is("could not read search request. unsupported non-empty array field [types]")); + assertThat(e.getMessage(), is("could not read search request. unexpected array field [types]")); } }